package com.yanqu.road.logic.activity.foxcard.game;

import com.alibaba.fastjson.JSONObject;
import com.yanqu.road.entity.activity.foxcard.*;
import com.yanqu.road.entity.activity.foxcard.config.*;
import com.yanqu.road.logic.activity.foxcard.FoxCardConfig;
import com.yanqu.road.logic.activity.foxcard.game.buff.FoxCardBuff;
import com.yanqu.road.logic.activity.foxcard.game.effect.FoxCardEffect;
import com.yanqu.road.logic.activity.foxcard.game.label.FoxCardLabel;
import com.yanqu.road.logic.activity.foxcard.game.sorcery.FoxCardSorceryUse;
import com.yanqu.road.logic.activity.foxcard.game.spell.FoxCardSpellUse;
import com.yanqu.road.utils.RandomHelper;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

import com.yanqu.road.pb.activity.FoxCardProto;
import com.yanqu.road.utils.classutil.ClassHelper;
import com.yanqu.road.utils.string.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class FoxCardGameLogic {
    public static final int CARD_COLOR_NUM = 4;
    static Map<String, Class<?>> classMap = new HashMap<>();
    static BigInteger maxDamageLimit;

    static {
        try {
            Package gamePackage = FoxCardGameLogic.class.getPackage();
            Set<Class<?>> classes = ClassHelper.getClasses(gamePackage);
            for (Class<?> aClass : classes) {
                classMap.put(aClass.getSimpleName(), aClass);
            }

            String maxLimitStr = "";
            for (int i = 0; i < 255; i++) {
                maxLimitStr+="9";
            }
            maxDamageLimit = new BigInteger(maxLimitStr);
        } catch (Exception e) {

        }
    }

    private static Logger logger = LogManager.getLogger(FoxCardGameLogic.class.getName());

    private FoxCardConfig config;
    private UserFoxCardGameData gameData;
    private List<FoxHandCard> addHandCards = new ArrayList<>();//新发的手牌
    private List<FoxHandCard> dropHandCards = new ArrayList<>();//丢弃的手牌
    private List<FoxHandCard> destroyHandCards = new ArrayList<>();//摧毁的手牌
    private List<FoxHandCard> showHandCards = new ArrayList<>();//打出的手牌
    private BigInteger nowBaseScore = BigInteger.ZERO; //当前筹码
    private BigInteger nowBaseMulti = BigInteger.ZERO; //当前倍率
    private BigInteger finalScore = BigInteger.ZERO; //最终得分
    private int effectGoldAmount; //效果金币统计
    private int interestGold; //利息金币
    private int showTimesGold;
    private int passGold;
    private int lastUseConsumeType; //最近一次使用的消耗牌类型

    private FoxCardCombo nowCombo; //当前牌型 null表示还没计算

    private List<FoxCardShowInfo> showInfos = new ArrayList<>();//表现过程

    public FoxCardGameLogic(FoxCardConfig config, UserFoxCardGameData gameData) {
        this.config = config;
        this.gameData = gameData;
    }

    public FoxCardConfig getConfig() {
        return config;
    }

    public UserFoxCardGameData getGameData() {
        return gameData;
    }

    public List<FoxHandCard> getAddHandCards() {
        return addHandCards;
    }

    public List<FoxHandCard> getDropHandCards() {
        return dropHandCards;
    }

    public List<FoxHandCard> getDestroyHandCards() {
        return destroyHandCards;
    }

    public int getLastUseConsumeType() {
        return lastUseConsumeType;
    }

    public void setLastUseConsumeType(int lastUseConsumeType) {
        this.lastUseConsumeType = lastUseConsumeType;
        dealCardEffect(eFoxCardTriggerType.USE_CONSUME_CARD);
    }

    public List<FoxCardShowInfo> getShowInfos() {
        List<FoxCardShowInfo> filterList = new ArrayList<>();
        for (FoxCardShowInfo showInfo : showInfos) {
            if (showInfo.getShowType() == eFoxCardShowType.EFFECT_CARD_LV_UP.getValue()) {
                //复制牌的效果升级过滤掉
                boolean isCopyCardShow = false;
                if (showInfo.getSettleCards().size() == 1) {
                    int cardId = showInfo.getSettleCards().get(0).get(0);
                    for (FoxEffectCard effectCard : gameData.getEffectCards()) {
                        if (effectCard.getId() == cardId) {
                            int configId = effectCard.getConfigId();
                            int effectType = config.getEffectCardConfigMap().get(configId).getType();
                            if (effectType == 23 || effectType == 24) {
                                isCopyCardShow = true;
                                break;
                            }
                        }
                    }
                }
                if (isCopyCardShow) {
                    continue;
                }
            }
            filterList.add(showInfo);
        }
        return filterList;
    }

    public BigInteger getNowBaseScore() {
        return nowBaseScore;
    }

    public void setNowBaseScore(BigInteger nowBaseScore) {
        this.nowBaseScore = nowBaseScore;
    }

    public BigInteger getNowBaseMulti() {
        return nowBaseMulti;
    }

    public void setNowBaseMulti(BigInteger nowBaseMulti) {
        this.nowBaseMulti = nowBaseMulti;
    }

    public List<FoxHandCard> getShowHandCards() {
        return showHandCards;
    }

    public FoxCardCombo getNowCombo() {
        return nowCombo;
    }

    public BigInteger getFinalScore() {
        return finalScore;
    }

    public int getEffectGoldAmount() {
        return effectGoldAmount;
    }

    public int getInterestGold() {
        return interestGold;
    }

    public int getShowTimesGold() {
        return showTimesGold;
    }

    public int getPassGold() {
        return passGold;
    }

    public <T> T getCardInterfaceInstance(String className) throws IllegalAccessException, InstantiationException {
        Class<?> aClass = classMap.get(className);
        return (T) aClass.newInstance();
    }

    ///////////////////////////////////////////////////////////////////////////////////////
    public UserFoxCardGameData createNewGameData(int stageId, int treeLevel) {
        FoxCardChapterConfig chapterConfig = config.getChapterConfigMap().get(stageId);
        FoxCardTreeLevelConfig treeLevelConfig = config.getTreeLevelConfig(treeLevel);

        UserFoxCardGameData gameData = new UserFoxCardGameData();
        this.gameData = gameData;
        gameData.setStageId(stageId);
        gameData.setRound(1);
        //初始手牌库 默认4种花色
        List<FoxHandCard> handCards = new ArrayList<>();
        for (Map.Entry<Integer, Integer> entry : config.getPointToScoreMap().entrySet()) {
            Integer point = entry.getKey();
            for (int color = 1; color <= CARD_COLOR_NUM; color++) {
                int cardId = gameData.generalCardUniqueId();
                FoxHandCard foxHandCard = new FoxHandCard(cardId, eFoxCardType.HAND.getValue(), config.getHandCardInitGold(), point, color);
                handCards.add(foxHandCard);
            }
        }
        gameData.setRemainHandCards(handCards);//设置牌库

        //初始参数(0|出牌次数|弃牌次数|手牌数量上限|小丑牌上限|消耗牌上限）
        gameData.setGold(treeLevelConfig.getGold());
        reCalcCardOpTimes();
        gameData.setEffectCardNumLimit(config.getGameInitParams().get(4));
        gameData.setConsumeCardNumLimit(config.getGameInitParams().get(5));

        //关卡效果额外加次数
        if (chapterConfig.getType() == 1) {
            //额外出牌次数|额外弃牌次数|额外金币|额外的小丑牌数量|额外的出牌次数
//            gameData.setShowCardTimesMax(gameData.getShowCardTimesMax() + chapterConfig.getShowTimesAdd());
//            gameData.setDropCardTimesMax(gameData.getDropCardTimesMax() + chapterConfig.getDropTimesAdd());
            gameData.setGold(gameData.getGold() + chapterConfig.getGoldAdd());
            gameData.setEffectCardNumLimit(gameData.getEffectCardNumLimit() + chapterConfig.getEffectCardLimitAdd());
        } else if (chapterConfig.getType() == 2) {//改变花色
            //参数1的花色变为参数2 参数3的花色变为参数4
            for (FoxHandCard handCard : handCards) {
                if (handCard.getColor() == chapterConfig.getParams().get(0)) {
                    handCard.setColor(chapterConfig.getParams().get(1));
                } else if (handCard.getColor() == chapterConfig.getParams().get(2)) {
                    handCard.setColor(chapterConfig.getParams().get(3));
                }
            }
            gameData.setRemainHandCards(handCards);
        }

        //初始牌型等级
        Map<Integer, List<Integer>> comboInfoMap = new HashMap<>();
        for (FoxCardMagicWeaponConfig weaponConfig : config.getComboConfigs()) {
            List<Integer> list = new ArrayList<>();
            list.add(treeLevelConfig.getComboLevel());
            list.add(0);
            comboInfoMap.put(weaponConfig.getId(), list);
        }
        gameData.setComboInfoMap(comboInfoMap);

        //生成无尽之前的所有怪物
        gameData.getMonsterMap().clear();
        int monsterNum = config.getFOX_ENDLESS_MODE_MISSION();
        List<UserFoxCardMonster> monsters = createMonsters(monsterNum);
        gameData.addMonsters(monsters);

        //生成商店数据
        gameData.setIsShopping(false);
        UserFoxCardShop shop = new UserFoxCardShop();
        FoxCardCouponConfig couponConfig = config.randomCoupon(new HashSet<>(gameData.getCoupons()));
        if (couponConfig != null) {
            FoxSaleCard coupon = new FoxSaleCard();
            coupon.setId(gameData.generalCardUniqueId());
            coupon.setConfigId(couponConfig.getId());
            coupon.setPrice(couponConfig.getGold());
            coupon.setType(eFoxCardType.COUPON.getValue());
            shop.setCoupon(coupon);
        } else {
            shop.setCoupon(null);
        }

        shop.setUpdateOption();
        gameData.setShop(shop);

        //初始卡
        for (Integer cardQuality : treeLevelConfig.getInitCards()) {
            if (cardQuality == 0) {
                continue;
            }
            List<FoxSaleCard> foxSaleCards = config.randomEffectCard(gameData.getEffectCards(), cardQuality, 1, -1, 0);
            if (foxSaleCards.isEmpty()) {
                continue;
            }
            FoxSaleCard saleCard = foxSaleCards.get(0);
            addNewEffectCard(saleCard.getConfigId(), saleCard.getVersionEffect());
        }

        return gameData;
    }

    public List<UserFoxCardMonster> createMonsters(int monsterNum) {
        List<UserFoxCardMonster> monsters = new ArrayList<>();
        for (int round = gameData.getRound(); round < gameData.getRound() + monsterNum; round++) {
            UserFoxCardMonster monster = new UserFoxCardMonster(round);
            FoxCardRoundConfig roundConfig = config.getRoundConfigMap().get(round);

            monster.setHp(roundConfig.getPassScore());
            if (roundConfig.getIsLimit() == 1) {
                int bossLimit = config.randomOnceBossLimit(round, gameData.getStageId());
                monster.setBossLimitEffect(bossLimit);
                FoxCardLimitConfig limitConfig = config.getLimitConfigMap().get(bossLimit);
                int hpMulti = 1000; //血量倍数
                if (limitConfig.getType() == eFoxCardLimitType.LIMIT2.getValue() || limitConfig.getType() == eFoxCardLimitType.LIMIT4.getValue()) {
                    hpMulti = limitConfig.getParamsList().get(3);
                }
                BigInteger newHp = new BigDecimal(monster.getHp()).multiply(BigDecimal.valueOf(hpMulti).divide(BigDecimal.valueOf(1000))).toBigInteger();
                monster.setHp(newHp);
            } else {
                int skipEffect = config.randomOnceLabel();
                monster.setSkipEffect(skipEffect);
            }

            monsters.add(monster);
        }
        return monsters;
    }

    /**
     * 发手牌
     */
    public FoxCardGameLogic addHandCards() {
        if (gameData.getHandCards().size() >= gameData.getHandCardNumLimit()) {//手牌已满
            return this;
        }
        int addNum = gameData.getHandCardNumLimit() - gameData.getHandCards().size();
        if (addNum <= 0) {
            return this;
        }
        addHandCards = RandomHelper.getRandomList(gameData.getRemainHandCards(), addNum);
        gameData.getHandCards().addAll(addHandCards);
        gameData.getRemainHandCards().removeAll(addHandCards);

        List<FoxHandCard> handCards = new ArrayList<>(gameData.getHandCards());
        //先把石头牌拿出来
        List<FoxHandCard> stoneCards = new ArrayList<>();
        for (FoxHandCard showCard : handCards) {
            if (config.isNotPointAndNotColorHandCard(showCard.getReinforceEffect())) {//石头牌
                stoneCards.add(showCard);
                continue;
            }
        }
        handCards.removeAll(stoneCards);
        handCards.sort((o1, o2) -> {
            int score1 = config.getPointToScoreMap().get(o1.getPoint());
            int score2 = config.getPointToScoreMap().get(o2.getPoint());
            int compare = Integer.compare(score2, score1);
            if (compare == 0) {
                compare = Integer.compare(o2.getPoint(), o1.getPoint());
            }
            if (compare == 0) {
                compare = Integer.compare(o1.getColor(), o2.getColor());
            }
            if (compare == 0) {
                compare = Integer.compare(o1.getId(), o2.getId());
            }
            return compare;
        });//先按得分排,再按点数排,点数相同,按颜色排,颜色低的在前面
        handCards.addAll(stoneCards);
        gameData.setHandCards(handCards);

        return this;
    }

    public FoxCardGameLogic dropHandCards(List<Integer> dropCardIds) {
        //先找出手牌
        List<FoxHandCard> findHandCards = new ArrayList<>();
        for (Integer cardId : dropCardIds) {
            for (FoxHandCard handCard : gameData.getHandCards()) {
                if (handCard.getId() == cardId) {
                    findHandCards.add(handCard);
                    break;
                }
            }
        }
        this.dropHandCards = findHandCards; //本次操作的弃牌
        //从手牌中删除
        gameData.getHandCards().removeAll(this.dropHandCards);
        //加入弃牌库
        gameData.getDropHandCards().addAll(this.dropHandCards);
        //扣弃牌次数
        gameData.setDropCardTimes(gameData.getDropCardTimes() - 1);
        //累计弃牌次数
        gameData.setDropTimesAmount(gameData.getDropTimesAmount() + 1);
        for (FoxHandCard dropHandCard : dropHandCards) {
            int cardPoint = getHandCardNowPoint(dropHandCard);
            Integer times = gameData.getDropPointAmountMap().getOrDefault(cardPoint, 0);
            gameData.getDropPointAmountMap().put(cardPoint, times + 1);

            dealHandCardBuff(dropHandCard, eFoxCardTriggerType.DROP);//弃牌触发效果
        }

        //弃牌触发的效果
        //187 type=47 第一次弃牌为{0}张牌时，摧毁该牌并获得{5}金币
        //带有【前世印章】的牌被弃掉时，将额外获得{1}张{0}。（需要有消耗栏空位）
        //32升级每回合第一次弃掉的牌型
        //更新有弃牌效果的小丑牌效果值
        //18 每次弃掉指定牌 / 花色，可以获得效果
        dealCardEffect(eFoxCardTriggerType.DROP);

        //重新发牌
        addHandCards();

        return this;
    }

    public FoxCardEffect getCardEffect(FoxEffectCard effectCard, eFoxCardTriggerType triggerType) {
        FoxCardEffectCardConfig effectCardConfig = config.getEffectCardConfigMap().get(effectCard.getConfigId());
        if (effectCardConfig == null) {
            return null;
        }
        try {
            FoxCardEffect effect = getCardInterfaceInstance("FoxCardEffect" + effectCardConfig.getType());
            return effect.init(effectCardConfig, effectCard, triggerType);
        } catch (Exception e) {
            logger.error("取不到小丑卡的实现 {}={} err:{}", effectCard.getId(), effectCard.getConfigId(), e);
            return null;
        }
    }


    public void dealCardEffect(eFoxCardTriggerType triggerType) {
        List<FoxEffectCard> effectCards = new ArrayList<>(gameData.getEffectCards());
        for (FoxEffectCard effectCard : effectCards) {
            dealCardEffect(effectCard, triggerType);
        }
    }

    public void dealCardEffect(eFoxCardTriggerType triggerType, FoxHandCard handCard) {
        List<FoxEffectCard> effectCards = new ArrayList<>(gameData.getEffectCards());
        for (FoxEffectCard effectCard : effectCards) {
            dealCardEffect(effectCard, triggerType, handCard);
        }
    }

    /**
     * 小丑牌效果处理
     *
     * @param effectCard
     */
    public void dealCardEffect(FoxEffectCard effectCard, eFoxCardTriggerType triggerType) {
        dealCardEffect(effectCard, triggerType, null);
    }

    public void dealCardEffect(FoxEffectCard effectCard, eFoxCardTriggerType triggerType, FoxHandCard handCard) {
        if (gameData.getRoundLimitEffectCard().contains(effectCard.getId())) {//本回合失效的小丑牌
            return;
        }
        FoxCardEffect cardEffect = getCardEffect(effectCard, triggerType);
        if (cardEffect != null) {
            cardEffect.handle(this, handCard);
        }
    }

    public int dealLabel(eFoxCardTriggerType triggerType) {
        List<Integer> labels = new ArrayList<>(gameData.getSkipLabelHaves());
        for (Integer label : labels) {
            dealLabel(label, triggerType);
        }
        return 0;
    }

    /**
     * 标签效果处理
     *
     * @param labelId
     * @return
     */
    public int dealLabel(int labelId, eFoxCardTriggerType triggerType) {
        FoxCardLabel label = getLabel(labelId, triggerType);
        if (label != null) {
            return label.handle(this);
        }
        return 0;
    }

    private FoxCardLabel getLabel(int labelId, eFoxCardTriggerType triggerType) {
        FoxCardLabelConfig labelConfig = config.getLabelConfigMap().get(labelId);
        if (labelConfig == null) {
            return null;
        }
        try {
            FoxCardLabel label = getCardInterfaceInstance("FoxCardLabel" + labelConfig.getType());
            return label.init(labelConfig, triggerType);
        } catch (Exception e) {
            logger.error("取不到标签的实现 {}={} err:{}", labelId, labelConfig.getType(), e);
            return null;
        }
    }

    public FoxCardShowInfo newFoxShow(int showType) {
        FoxCardShowInfo showInfo = new FoxCardShowInfo(showType);
        showInfo.setNowBaseScore(nowBaseScore);
        showInfo.setNowBaseMulti(nowBaseMulti);

        if (finalScore.equals(BigInteger.ZERO)) { //最终分还没算,才临时计算
            BigDecimal nowBaseScoreDecimal = new BigDecimal(nowBaseScore).divide(BigDecimal.valueOf(1000)).setScale(1, BigDecimal.ROUND_HALF_UP);
            BigDecimal nowBaseMultiDecimal = new BigDecimal(nowBaseMulti).divide(BigDecimal.valueOf(1000)).setScale(1, BigDecimal.ROUND_HALF_UP);
            BigInteger damageAmount = nowBaseScoreDecimal.multiply(nowBaseMultiDecimal).toBigInteger().add(gameData.getDamageAmount());
            if (damageAmount.compareTo(maxDamageLimit) > 0) {
                damageAmount = maxDamageLimit;
            }
            showInfo.setDamageAmount(damageAmount);
        } else {
            showInfo.setDamageAmount(gameData.getDamageAmount());
        }

        showInfos.add(showInfo);

        return showInfo;
    }

    public FoxCardShowInfo addBaseScore(long value, List<FoxBaseCard> settleCards, List<FoxBaseCard> triggerCards) {
        if (value == 0) {
            return null;
        }
        BigInteger changeValue = BigInteger.valueOf(value).multiply(BigInteger.valueOf(1000));
        nowBaseScore = nowBaseScore.add(changeValue);

        FoxCardShowInfo showInfo = newFoxShow(eFoxCardShowType.ADD_BASE_SCORE.getValue());
        showInfo.setSettleCards(settleCards);
        showInfo.setTriggerCards(triggerCards);
        showInfo.setShowValue(changeValue.toString());

        return showInfo;
    }

    public FoxCardShowInfo addBaseScoreMulti(int value, List<FoxBaseCard> settleCards, List<FoxBaseCard> triggerCards) {
        if (value == 0) {
            return null;
        }
        BigInteger oldValue = nowBaseScore;
        int changeValue = 1000 + value;
        nowBaseScore = new BigDecimal(nowBaseScore.toString()).multiply(BigDecimal.valueOf(changeValue).divide(BigDecimal.valueOf(1000))).toBigInteger();

        FoxCardShowInfo showInfo = newFoxShow(eFoxCardShowType.ADD_BASE_SCORE_MULTI.getValue());
        showInfo.setSettleCards(settleCards);
        showInfo.setTriggerCards(triggerCards);
        showInfo.setShowValue(String.valueOf(changeValue));

        return showInfo;
    }

    public FoxCardShowInfo addBaseMulti(int value, List<FoxBaseCard> settleCards, List<FoxBaseCard> triggerCards) {
        if (value == 0) {
            return null;
        }
        BigInteger changeValue = BigInteger.valueOf(value).multiply(BigInteger.valueOf(1000));
        nowBaseMulti = nowBaseMulti.add(changeValue);

        FoxCardShowInfo showInfo = newFoxShow(eFoxCardShowType.ADD_BASE_MULTI.getValue());
        showInfo.setSettleCards(settleCards);
        showInfo.setTriggerCards(triggerCards);
        showInfo.setShowValue(changeValue.toString());

        return showInfo;
    }

    public FoxCardShowInfo addBaseMultiMulti(int value, List<FoxBaseCard> settleCards, List<FoxBaseCard> triggerCards) {
        if (value == 0) {
            return null;
        }
        int changeValue = 1000 + value;
        nowBaseMulti = new BigDecimal(nowBaseMulti).multiply(BigDecimal.valueOf(changeValue).divide(BigDecimal.valueOf(1000))).toBigInteger();

        FoxCardShowInfo showInfo = newFoxShow(eFoxCardShowType.ADD_BASE_MULTI_MULTI.getValue());
        showInfo.setSettleCards(settleCards);
        showInfo.setTriggerCards(triggerCards);
        showInfo.setShowValue(String.valueOf(changeValue));

        return showInfo;
    }

    public FoxCardShowInfo goldChange(int value, List<FoxBaseCard> settleCards, List<FoxBaseCard> triggerCards, eFoxCardTriggerType triggerType) {
        if (value == 0) {
            return null;
        }
        int oldGold = gameData.getGold();
        int newGold = gameData.getGold() + value;
        if (newGold < 0) {
            newGold = 0;
        }
        gameData.setGold(newGold);

        FoxCardShowInfo showInfo = newFoxShow(eFoxCardShowType.ADD_GOLD.getValue());
        showInfo.setSettleCards(settleCards);
        showInfo.setTriggerCards(triggerCards);
        showInfo.setShowValue(String.valueOf(newGold - oldGold));
        showInfo.setTriggerType(triggerType.getValue());

        if (settleCards != null && !settleCards.isEmpty() && value > 0) {
            effectGoldAmount += value;
        }
        return showInfo;
    }

    public void destroyHandCards(List<FoxHandCard> cards) {
        this.destroyHandCards = cards;

        gameData.getHandCards().removeAll(cards);
        gameData.getRemainHandCards().removeAll(cards);
        gameData.getDropHandCards().removeAll(cards);

        FoxCardShowInfo showInfo = newFoxShow(eFoxCardShowType.DEL_CARD.getValue());
        showInfo.setSettleCards(new ArrayList<>(cards));

        for (FoxHandCard handCard : cards) {
            dealHandCardBuff(handCard, eFoxCardTriggerType.REMOVE);

            dealCardEffect(eFoxCardTriggerType.REMOVE, handCard);
        }

        if (gameData.getHandCards().isEmpty()) {//摧毁所有手上的牌,要重新发
            addHandCards();
        }
    }

    public void destroyEffectCards(List<FoxEffectCard> cards) {
        gameData.getEffectCards().removeAll(cards);

        FoxCardShowInfo showInfo = newFoxShow(eFoxCardShowType.DEL_CARD.getValue());
        showInfo.setSettleCards(new ArrayList<>(cards));

        for (FoxEffectCard card : cards) {
            dealEffectCardBuff(card, eFoxCardTriggerType.REMOVE);
        }
    }

    public void destroyConsumeCards(List<FoxConsumeCard> cards) {
        gameData.getConsumeCards().removeAll(cards);

        FoxCardShowInfo showInfo = newFoxShow(eFoxCardShowType.DEL_CARD.getValue());
        showInfo.setSettleCards(new ArrayList<>(cards));

        for (FoxConsumeCard card : cards) {
            dealConsumeCardBuff(card, eFoxCardTriggerType.REMOVE);
        }
    }

    public FoxCardGameLogic skipBattle() {
        //更新到下一个回合
        nextRound();

        UserFoxCardMonster monster = gameData.getMonster(gameData.getRound() - 1);
        if (monster.getSkipEffect() != 0) {
            gameData.getSkipLabelHaves().add(monster.getSkipEffect());
            dealLabel(eFoxCardTriggerType.SKIP);
        }

        gameData.setSkipTimesAmount(gameData.getSkipTimesAmount() + 1);

        return this;
    }

    /**
     * 出牌
     *
     * @param showCardIds
     * @return 0继续出牌 1胜利 2失败 3进入无尽模式 4通关
     */
    public int showHandCards(List<Integer> showCardIds, List<Integer> equipCards, List<Integer> unlockCards) {
        //先找出手牌
        List<FoxHandCard> findHandCards = new ArrayList<>();
        for (Integer cardId : showCardIds) {
            for (FoxHandCard handCard : gameData.getHandCards()) {
                if (handCard.getId() == cardId) {
                    findHandCards.add(handCard);
                    break;
                }
            }
        }
        this.showHandCards = findHandCards; //本次操作的出牌
        //从手牌中删除
        gameData.getHandCards().removeAll(this.showHandCards);
        //加入打出牌
        gameData.getDropHandCards().addAll(this.showHandCards);
        //扣出牌次数
        gameData.setShowCardTimes(gameData.getShowCardTimes() - 1);
        //累计出牌次数
        gameData.setShowTimesAmount(gameData.getShowTimesAmount() + 1);

        //计算牌型
        nowCombo = calcShowCardCombo(showHandCards);

        if (!isBossLimitCalcScore()) {
            //累计点数出牌次数
            for (FoxHandCard showHandCard : nowCombo.getHandCards()) {
                int point = getHandCardNowPoint(showHandCard);
                int times = gameData.getShowPointAmountMap().getOrDefault(point, 0);
                gameData.getShowPointAmountMap().put(point, times + 1);
            }
            //先算牌型分
            List<Long> comboValues = config.calcComboBaseValues(nowCombo.getComboId(), nowCombo.getLevel(), gameData.getBossLimitEffect());
            nowBaseScore = BigInteger.valueOf(comboValues.get(0)).multiply(BigInteger.valueOf(1000));
            nowBaseMulti = BigInteger.valueOf(comboValues.get(1)).multiply(BigInteger.valueOf(1000));

            //先处理BOSS限制效果
            dealBossLimit();

            //先触发计分前效果
            dealCardEffect(eFoxCardTriggerType.BEFORE_SCORE);

            //牌型计分
            dealComboScore();

            for (FoxHandCard handCard : nowCombo.getHandCards()) {
                dealHandCardBuff(handCard, eFoxCardTriggerType.AFTER_SCORE);//玻璃牌,计分后概率碎掉
            }

            //分数结算后触发剩余手牌
            for (FoxHandCard handCard : gameData.getHandCards()) {
                dealHandCardBuff(handCard, eFoxCardTriggerType.HOLD_HAND_CARD);//持有效果
                dealCardEffect(eFoxCardTriggerType.HOLD_HAND_CARD, handCard);
            }

            //再触发计分后效果
            dealCardEffect(eFoxCardTriggerType.AFTER_SCORE);

            gameData.setRoundLastShowCombo(nowCombo.getComboId());
            gameData.getRoundShowCombos().add(nowCombo.getComboId());
        }

        //获得最后得分
        BigDecimal nowBaseScoreDecimal = new BigDecimal(nowBaseScore).divide(BigDecimal.valueOf(1000)).setScale(1, BigDecimal.ROUND_HALF_UP);
        BigDecimal nowBaseMultiDecimal = new BigDecimal(nowBaseMulti).divide(BigDecimal.valueOf(1000)).setScale(1, BigDecimal.ROUND_HALF_UP);
        finalScore = nowBaseScoreDecimal.multiply(nowBaseMultiDecimal).toBigInteger();
        if (finalScore.compareTo(maxDamageLimit) > 0) {
            finalScore = maxDamageLimit;
        }
        if (finalScore.compareTo(gameData.getMaxOnceDamage()) > 0) {
            gameData.setMaxOnceDamage(finalScore);
        }

        gameData.setDamageAmount(gameData.getDamageAmount().add(finalScore));
        if (gameData.getDamageAmount().compareTo(maxDamageLimit) > 0) {
            gameData.setDamageAmount(maxDamageLimit);
        }
        int remainShowCardTimes = gameData.getShowCardTimes();

        if (gameData.getDamageAmount().compareTo(gameData.getMonster(gameData.getRound()).getHp()) >= 0) {//通关,进入购物模式
            if (gameData.getRound() >= config.getRoundConfigMap().size()) {
                //通关,没有下一关了
                return 4;
            } else {
                for (FoxHandCard handCard : gameData.getHandCards()) {
                    dealHandCardBuff(handCard, eFoxCardTriggerType.ROUND_END);//回合结束持有
                }
                //小丑牌每回合获得金币
                dealCardEffect(eFoxCardTriggerType.ROUND_END);
                dealLabel(eFoxCardTriggerType.ROUND_END);

                //计算结束获得的金币
                int maxInterestGold = calcMaxInterestGold();
                interestGold = config.calcInterest(gameData.getGold(), maxInterestGold);
                showTimesGold = remainShowCardTimes;
                passGold = config.getRoundConfigMap().get(gameData.getRound()).getGoldReward();
//                goldChange(interestGold, null, null, eFoxCardTriggerType.ROUND_END);
//                goldChange(showTimesGold, null, null, eFoxCardTriggerType.ROUND_END);
//                goldChange(passGold, null, null, eFoxCardTriggerType.ROUND_END);
                gameData.setGold(gameData.getGold() + interestGold + showTimesGold + passGold);

                gameData.setIsShopping(true);
                gameShopInit(equipCards, unlockCards);//商店初始化

                if (gameData.getRound() == config.getFOX_ENDLESS_MODE_MISSION()) {//进入无尽模式
                    //生成无尽的怪
                    int monsterNum = config.getRoundConfigMap().size() - config.getFOX_ENDLESS_MODE_MISSION();
                    List<UserFoxCardMonster> monsters = createMonsters(monsterNum + 1);
                    gameData.addMonsters(monsters);
                    return 3;
                }
                return 1;
            }
        } else {
            if (remainShowCardTimes > 0) {
                addHandCards();//继续发牌
                //极端情况, 没手牌了, 算失败
                if (gameData.getHandCards().isEmpty()) {
                    gameData.setStageId(0);
                    gameData.setRound(0);
                    gameData.setInBattle(false);
                    gameData.setIsShopping(false);
                    return 2;
                }
                return 0;
            } else {//没有出牌次数了,失败,重置
                gameData.setStageId(0);
                gameData.setRound(0);
                gameData.setInBattle(false);
                gameData.setIsShopping(false);
                return 2;
            }
        }
    }

    /**
     * boss限制计算所有效果和计分
     * @return
     */
    public boolean isBossLimitCalcScore() {
        boolean isLimit = false;
        FoxCardLimitConfig limitConfig = config.getLimitConfigMap().get(gameData.getBossLimitEffect());
        if (limitConfig != null) {
            if (limitConfig.getType() == eFoxCardLimitType.LIMIT8.getValue()) {//出牌数量限制死
                Integer limitNum = limitConfig.getParamsList().get(0);
                if (showHandCards.size() != limitNum) {
                    isLimit = true;
                }
            } else if (limitConfig.getType() == eFoxCardLimitType.LIMIT9.getValue()) {//只能打一种牌型
                if (gameData.getRoundLastShowCombo() != 0 && gameData.getRoundLastShowCombo() != nowCombo.getComboId()) {
                    isLimit = true;
                }
            } else if (limitConfig.getType() == eFoxCardLimitType.LIMIT14.getValue()) {//重复打出的牌型不计分
                if (gameData.getRoundShowCombos().contains(nowCombo.getComboId())) {
                    isLimit = true;
                }
            } else if (limitConfig.getType() == eFoxCardLimitType.LIMIT7.getValue()) {//有这个限制扑克牌不计分
                isLimit = true;
            }
        }

        if (isLimit) {
            newFoxShow(eFoxCardShowType.BOSS_LIMIT.getValue()).setShowValue(String.valueOf(limitConfig.getId()));
        }
        return isLimit;
    }

    public void dealBossLimit() {
        FoxCardLimitConfig limitConfig = config.getLimitConfigMap().get(gameData.getBossLimitEffect());
        if (limitConfig != null) {
            if (limitConfig.getType() == eFoxCardLimitType.LIMIT12.getValue()) {
                newFoxShow(eFoxCardShowType.BOSS_LIMIT.getValue()).setShowValue(String.valueOf(limitConfig.getId()));
                addBaseScoreMulti(limitConfig.getParamsList().get(0), null, null);
                addBaseMultiMulti(limitConfig.getParamsList().get(1), null, null);
            } else if (limitConfig.getType() == eFoxCardLimitType.LIMIT5.getValue()) {//boss限制5,打出最高等级牌型时,金币归0
                int maxLevelComboId = getMaxLevelComboId();
                if (nowCombo.getComboId() == maxLevelComboId && gameData.getGold() > 0) {
                    newFoxShow(eFoxCardShowType.BOSS_LIMIT.getValue()).setShowValue(String.valueOf(limitConfig.getId()));
                    goldChange(gameData.getGold() * -1, null, null, eFoxCardTriggerType.BEFORE_SCORE);
                }
            } else if (limitConfig.getType() == eFoxCardLimitType.LIMIT10.getValue()) {//和策划确认过,这里的弃牌不触发弃牌效果
                if (!gameData.getHandCards().isEmpty()) {
                    Integer dropNum = limitConfig.getParamsList().get(0);
                    List<FoxHandCard> dropList = RandomHelper.getRandomList(gameData.getHandCards(), dropNum);
                    if (!dropList.isEmpty()) {
                        //从手牌中删除
                        gameData.getHandCards().removeAll(dropList);
                        //加入弃牌库
                        gameData.getDropHandCards().addAll(dropList);

                        newFoxShow(eFoxCardShowType.BOSS_LIMIT.getValue()).setShowValue(String.valueOf(limitConfig.getId())).setSettleCards(new ArrayList<>(dropList));
                    }
                }
            } else if (limitConfig.getType() == eFoxCardLimitType.LIMIT11.getValue()) {//随机一张小丑牌失效
                if (!gameData.getEffectCards().isEmpty()) {
                    List<FoxEffectCard> filterList = new ArrayList<>();
                    for (FoxEffectCard effectCard : gameData.getEffectCards()) {
                        if (!gameData.getRoundLimitEffectCard().contains(effectCard.getId())) {
                            filterList.add(effectCard);
                        }
                    }
                    if (!filterList.isEmpty()) {
                        FoxEffectCard effectCard = RandomHelper.getRandomList(filterList, 1).get(0);
                        gameData.getRoundLimitEffectCard().add(effectCard.getId());
                        newFoxShow(eFoxCardShowType.BOSS_LIMIT.getValue()).setShowValue(String.valueOf(limitConfig.getId())).setSettleCards(Collections.singletonList(effectCard));
                    }
                }
            } else if (limitConfig.getType() == eFoxCardLimitType.LIMIT13.getValue()) {//每打出x张牌扣y金币
                int showNum = showHandCards.size();
                int perNum = Math.max(1, limitConfig.getParamsList().get(0));
                int totalTimes = showNum / perNum;
                int decGold = totalTimes * limitConfig.getParamsList().get(1);
                decGold = Math.min(gameData.getGold(), decGold);
                if (gameData.getGold() >= decGold) {
                    newFoxShow(eFoxCardShowType.BOSS_LIMIT.getValue()).setShowValue(String.valueOf(limitConfig.getId()));
                    goldChange(-1 * decGold, null, null, eFoxCardTriggerType.BEFORE_SCORE);
                }
            }
        }
    }

    /**
     * 牌型计分
     */
    public void dealComboScore() {
        if (nowCombo == null) {
            return;
        }
        for (FoxHandCard handCard : nowCombo.getHandCards()) {
            int pointScore = config.getPointScore(getHandCardNowPoint(handCard));
            addBaseScore(pointScore, Collections.singletonList(handCard), null);

            dealHandCardBuff(handCard, eFoxCardTriggerType.SCORE);//计算强化效果 版本效果 印章效果

            dealCardEffect(eFoxCardTriggerType.SCORE, handCard);//手牌计分时触发的小丑牌
        }

        int showTimes = getHistoryComboShowTimes(nowCombo.getComboId());
        gameData.getComboInfoMap().get(nowCombo.getComboId()).set(1, showTimes + 1);
    }

    /**
     * 计算最大利息
     *
     * @return
     */
    private int calcMaxInterestGold() {
        int maxValue = config.getGoldInterestMax();
        for (Integer coupon : gameData.getCoupons()) {
            FoxCardCouponConfig couponConfig = config.getCouponConfigMap().get(coupon);
            if (couponConfig == null) {
                continue;
            }
            if (couponConfig.getType() == eFoxCardCouponType.COUPON9.getValue()) {
                Integer configValue = couponConfig.getParamList().get(1);
                if (configValue > maxValue) {
                    maxValue = configValue;
                }
            }
        }
        return maxValue;
    }

    private boolean limitCalcScore(FoxHandCard handCard) {
        int bossLimitEffect = gameData.getBossLimitEffect();
        if (bossLimitEffect == 0) {
            return false;
        }
        FoxCardLimitConfig limitConfig = config.getLimitConfigMap().get(bossLimitEffect);
        if (limitConfig != null && limitConfig.getType() == eFoxCardLimitType.LIMIT1.getValue()) {//指定花色不计分
            int limitColor = limitConfig.getParamsList().get(0);
            if (config.isAllColorHandCard(handCard.getReinforceEffect())) {
                return false;
            }
            if (config.isNotPointAndNotColorHandCard(handCard.getReinforceEffect())) {
                return false;
            }
            if (handCard.getColor() == limitColor) {
                return true;
            }
        }
        return false;
    }

    /**
     * 计算强化效果 版本效果 印章效果
     *
     * @param handCard
     */
    public void dealHandCardBuff(FoxHandCard handCard, eFoxCardTriggerType triggerType) {
        if (handCard.getReinforceEffect() > 0) {
            FoxCardBuff buff = getCardBuff(handCard, handCard.getReinforceEffect(), triggerType);
            if (buff != null) {
                buff.handle(this);
            }
        }
        if (handCard.getVersionEffect() > 0) {
            FoxCardBuff buff = getCardBuff(handCard, handCard.getVersionEffect(), triggerType);
            if (buff != null) {
                buff.handle(this);
            }
        }
        if (handCard.getSealEffect() > 0) {
            FoxCardBuff buff = getCardBuff(handCard, handCard.getSealEffect(), triggerType);
            if (buff != null) {
                buff.handle(this);
            }
        }
    }

    /**
     * 手牌重复触发,不能触发印章
     *
     * @param handCard
     * @param triggerType
     */
    public void repeatDealHandCardBuff(FoxHandCard handCard, eFoxCardTriggerType triggerType) {
        if (triggerType == eFoxCardTriggerType.SCORE) {
            int pointScore = config.getPointScore(getHandCardNowPoint(handCard));
            addBaseScore(pointScore, Collections.singletonList(handCard), null);
        }

        if (handCard.getReinforceEffect() > 0) {
            FoxCardBuff buff = getCardBuff(handCard, handCard.getReinforceEffect(), triggerType);
            if (buff != null) {
                buff.handle(this);
            }
        }
        if (handCard.getVersionEffect() > 0) {
            FoxCardBuff buff = getCardBuff(handCard, handCard.getVersionEffect(), triggerType);
            if (buff != null) {
                buff.handle(this);
            }
        }

        dealCardEffect(eFoxCardTriggerType.SCORE, handCard);//手牌计分时触发的小丑牌
    }


    public void dealEffectCardBuff(FoxEffectCard effectCard, eFoxCardTriggerType triggerType) {
        if (gameData.getRoundLimitEffectCard().contains(effectCard.getId())) {//本回合失效的小丑牌
            return;
        }
        if (effectCard.getVersionEffect() > 0) {
            FoxCardBuff buff = getCardBuff(effectCard, effectCard.getVersionEffect(), triggerType);
            if (buff != null) {
                buff.handle(this);
            }
        }
    }

    public void dealConsumeCardBuff(FoxConsumeCard consumeCard, eFoxCardTriggerType triggerType) {
        if (consumeCard.getVersionEffect() > 0) {
            FoxCardBuff buff = getCardBuff(consumeCard, consumeCard.getVersionEffect(), triggerType);
            if (buff != null) {
                buff.handle(this);
            }
        }
    }

    public FoxCardBuff getCardBuff(FoxBaseCard baseCard, int buffId, eFoxCardTriggerType triggerType) {
        if (buffId > 0) {
            FoxCardBuffConfig buffConfig = config.getBuffConfigMap().get(buffId);
            if (buffConfig != null) {
                try {
                    FoxCardBuff buff = getCardInterfaceInstance("FoxCardBuff" + buffConfig.getSkillType());
                    return buff.init(buffConfig, baseCard, triggerType);
                } catch (Exception e) {
                    logger.error("取不到buff的实现 {}={} err:{}", buffId, buffConfig.getSkillType(), e);
                    return null;
                }
            }
        }
        return null;
    }

    /**
     * 计算牌型
     */
    public FoxCardCombo calcShowCardCombo(List<FoxHandCard> cards) {
        List<FoxHandCard> showCards = new ArrayList<>(cards);
        //先把石头牌拿出来
        List<FoxHandCard> resultCards = new ArrayList<>();
        List<FoxHandCard> stoneCards = new ArrayList<>();
        for (FoxHandCard showCard : showCards) {
            if (config.isNotPointAndNotColorHandCard(showCard.getReinforceEffect())) {//石头牌
                stoneCards.add(showCard);
                continue;
            }
        }
        showCards.removeAll(stoneCards);

        showCards.sort((o1, o2) -> {
            int score1 = config.getPointToScoreMap().get(o1.getPoint());
            int score2 = config.getPointToScoreMap().get(o2.getPoint());
            int compare = Integer.compare(score2, score1);
            if (compare == 0) {
                compare = Integer.compare(o2.getPoint(), o1.getPoint());
            }
            return compare;
        });//先按得分排,再按点数排

        //顺子可以间隔的点数,默认一点
        int straightInterval = 1;
        //哪些颜色可以视为同一花色
        Map<Integer, Set<Integer>> sameColorMap = new HashMap<>();
        sameColorMap.put(0, new HashSet<>()); //0和所有花色都是同花色
        for (int color = 1; color <= CARD_COLOR_NUM; color++) {
            Set<Integer> sameColors = new HashSet<>();
            sameColors.add(color);
            sameColorMap.put(color, sameColors);

            sameColorMap.get(0).add(color);
        }
        //顺子和花色的最小数量要求
        int straightMinNum = 5;

        for (FoxEffectCard effectCard : gameData.getEffectCards()) {//改变牌型判定的效果
            FoxCardEffectCardConfig effectCardConfig = config.getEffectCardConfigMap().get(effectCard.getConfigId());
            if (effectCardConfig == null) {
                continue;
            }
            if (effectCardConfig.getType() == 30) {//同花和顺子都可以由4张牌组成
                straightMinNum = 4;
            } else if (effectCardConfig.getType() == 31) {//顺子可以相隔1个点数
                straightInterval = 2;
            } else if (effectCardConfig.getType() == 33) {//{0}视为同一种花色；{1}也视为同一种花色
                List<Integer> list1 = StringUtils.stringToIntegerList(effectCardConfig.getParamsList().get(0), ";");
                List<Integer> list2 = StringUtils.stringToIntegerList(effectCardConfig.getParamsList().get(1), ";");
                for (Integer color : list1) {
                    Set<Integer> sameColors = sameColorMap.get(color);
                    sameColors.addAll(list1);
                }
                for (Integer color : list2) {
                    Set<Integer> sameColors = sameColorMap.get(color);
                    sameColors.addAll(list2);
                }
            }
        }

        int findComboId = 0;

        for (FoxCardMagicWeaponConfig comboConfig : config.getComboConfigs()) {
            if (findComboId != 0) {//找到牌型了
                break;
            }
            switch (comboConfig.getId()) {
                case FoxCardProto.eFoxCardComboType.STRAIGHT_FLUSH_VALUE: {//同花顺
                    if (showCards.size() < straightMinNum) {
                        break;
                    }
                    List<FoxHandCard> straightCard = config.getStraightCards(showCards, straightInterval, straightMinNum);
                    if (straightCard.isEmpty()) { //没找到顺子
                        break;
                    }
                    List<FoxHandCard> sameColorCards = config.getSameColorCards(showCards, sameColorMap, straightMinNum);
                    if (sameColorCards.isEmpty()) {//没找到同花
                        break;
                    }
                    Set<FoxHandCard> findCards = new HashSet<>();//去重一下
                    findCards.addAll(straightCard);
                    findCards.addAll(sameColorCards);
                    findComboId = comboConfig.getId();
                    resultCards.addAll(findCards);
                }
                break;
                case FoxCardProto.eFoxCardComboType.STRAIGHT_VALUE: {//顺子
                    if (showCards.size() < straightMinNum) {
                        break;
                    }
                    List<FoxHandCard> straightCard = config.getStraightCards(showCards, straightInterval, straightMinNum);
                    if (straightCard.isEmpty()) { //没找到顺子
                        break;
                    }
                    findComboId = comboConfig.getId();
                    resultCards.addAll(straightCard);
                }
                break;
                case FoxCardProto.eFoxCardComboType.FLUSH_VALUE: {//同花
                    if (showCards.size() < straightMinNum) {
                        break;
                    }
                    List<FoxHandCard> sameColorCards = config.getSameColorCards(showCards, sameColorMap, straightMinNum);
                    if (sameColorCards.isEmpty()) {//没找到同花
                        break;
                    }
                    findComboId = comboConfig.getId();
                    resultCards.addAll(sameColorCards);
                }
                case FoxCardProto.eFoxCardComboType.FOUR_OF_A_KIND_VALUE: {//四条
                    if (showCards.size() < 4) {
                        break;
                    }
                    Set<FoxHandCard> findCards = new HashSet<>();
                    for (int i = 0; i < showCards.size() - 3; i++) {
                        FoxHandCard nowCard = showCards.get(i);
                        FoxHandCard nextCard = showCards.get(i + 1);
                        FoxHandCard nextNextCard = showCards.get(i + 2);
                        FoxHandCard nextNextNextCard = showCards.get(i + 3);
                        if (nowCard.getPoint() == nextCard.getPoint() && nowCard.getPoint() == nextNextCard.getPoint() && nowCard.getPoint() == nextNextNextCard.getPoint()) {
                            findCards.add(nowCard);
                            findCards.add(nextCard);
                            findCards.add(nextNextCard);
                            findCards.add(nextNextNextCard);
                        }
                    }
                    if (findCards.isEmpty()) {
                        break;
                    }
                    findComboId = comboConfig.getId();
                    resultCards.addAll(findCards);
                }
                break;
                case FoxCardProto.eFoxCardComboType.FULL_HOUSE_VALUE: {//葫芦
                    if (showCards.size() < 5) {
                        break;
                    }
                    Set<FoxHandCard> findCards = new HashSet<>();
                    for (int i = 0; i < showCards.size() - 2; i++) {
                        FoxHandCard nowCard = showCards.get(i);
                        FoxHandCard nextCard = showCards.get(i + 1);
                        FoxHandCard nextNextCard = showCards.get(i + 2);
                        if (nowCard.getPoint() == nextCard.getPoint() && nowCard.getPoint() == nextNextCard.getPoint()) {
                            findCards.add(nowCard);
                            findCards.add(nextCard);
                            findCards.add(nextNextCard);
                            break;
                        }
                    }
                    if (findCards.isEmpty()) {
                        break;
                    }
                    for (int i = 0; i < showCards.size() - 1; i++) {
                        FoxHandCard nowCard = showCards.get(i);
                        FoxHandCard nextCard = showCards.get(i + 1);
                        if (nowCard.getPoint() == nextCard.getPoint() && !findCards.contains(nowCard)) {
                            findCards.add(nowCard);
                            findCards.add(nextCard);
                            break;
                        }
                    }
                    if (findCards.size() < 5) {
                        break;
                    }
                    findComboId = comboConfig.getId();
                    resultCards.addAll(findCards);
                }
                break;
                case FoxCardProto.eFoxCardComboType.THREE_OF_A_KIND_VALUE: {//三条
                    if (showCards.size() < 3) {
                        break;
                    }
                    Set<FoxHandCard> findCards = new HashSet<>();
                    for (int i = 0; i < showCards.size() - 2; i++) {
                        FoxHandCard nowCard = showCards.get(i);
                        FoxHandCard nextCard = showCards.get(i + 1);
                        FoxHandCard nextNextCard = showCards.get(i + 2);
                        if (nowCard.getPoint() == nextCard.getPoint() && nowCard.getPoint() == nextNextCard.getPoint()) {
                            findCards.add(nowCard);
                            findCards.add(nextCard);
                            findCards.add(nextNextCard);
                            break;
                        }
                    }
                    if (findCards.isEmpty()) {
                        break;
                    }
                    findComboId = comboConfig.getId();
                    resultCards.addAll(findCards);
                }
                break;
                case FoxCardProto.eFoxCardComboType.TWO_PAIR_VALUE: {//两对
                    if (showCards.size() < 4) {
                        break;
                    }
                    Set<FoxHandCard> findCards = new HashSet<>();
                    for (int i = 0; i < showCards.size() - 1; i++) {
                        FoxHandCard nowCard = showCards.get(i);
                        FoxHandCard nextCard = showCards.get(i + 1);
                        if (nowCard.getPoint() == nextCard.getPoint()) {
                            findCards.add(nowCard);
                            findCards.add(nextCard);
                            break;
                        }
                    }
                    if (findCards.size() < 2) {
                        break;
                    }
                    for (int i = 0; i < showCards.size() - 1; i++) {
                        FoxHandCard nowCard = showCards.get(i);
                        FoxHandCard nextCard = showCards.get(i + 1);
                        if (nowCard.getPoint() == nextCard.getPoint() && !findCards.contains(nowCard)) {
                            findCards.add(nowCard);
                            findCards.add(nextCard);
                            break;
                        }
                    }
                    if (findCards.size() < 4) {
                        break;
                    }
                    findComboId = comboConfig.getId();
                    resultCards.addAll(findCards);
                }
                break;
                case FoxCardProto.eFoxCardComboType.ONE_PAIR_VALUE: {//一对
                    if (showCards.size() < 2) {
                        break;
                    }
                    Set<FoxHandCard> findCards = new HashSet<>();
                    for (int i = 0; i < showCards.size() - 1; i++) {
                        FoxHandCard nowCard = showCards.get(i);
                        FoxHandCard nextCard = showCards.get(i + 1);
                        if (nowCard.getPoint() == nextCard.getPoint()) {
                            findCards.add(nowCard);
                            findCards.add(nextCard);
                            break;
                        }
                    }
                    if (findCards.isEmpty()) {
                        break;
                    }
                    findComboId = comboConfig.getId();
                    resultCards.addAll(findCards);
                }
                break;
                case FoxCardProto.eFoxCardComboType.HIGH_CARD_VALUE: {//散牌
                    if (showCards.size() < 1) {
                        break;
                    }
                    findComboId = comboConfig.getId();
                    resultCards.add(showCards.get(0));
                }
                break;
            }
        }
        if (findComboId == 0) {
            findComboId = FoxCardProto.eFoxCardComboType.HIGH_CARD_VALUE;
        }
        int level = 0;
        List<Integer> list = gameData.getComboInfoMap().get(findComboId);
        if (list != null) {
            level = list.get(0);
        }

        resultCards.sort((o1, o2) -> {
            int score1 = config.getPointToScoreMap().get(o1.getPoint());
            int score2 = config.getPointToScoreMap().get(o2.getPoint());
            int compare = Integer.compare(score2, score1);
            if (compare == 0) {
                compare = Integer.compare(o2.getPoint(), o1.getPoint());
            }
            if (compare == 0) {
                compare = Integer.compare(o1.getColor(), o2.getColor());
            }
            if (compare == 0) {
                compare = Integer.compare(o1.getId(), o2.getId());
            }
            return compare;
        });//先按得分排,再按点数排,点数相同,按颜色排,颜色低的在前面
        resultCards.addAll(stoneCards);

        List<FoxHandCard> filterCards = new ArrayList<>();
        for (FoxHandCard handCard : resultCards) {
            if (limitCalcScore(handCard)) {//判断一下限制计分的花色,不计分不触发效果
                continue;
            }
            filterCards.add(handCard);
        }
        FoxCardCombo foxCardCombo = new FoxCardCombo(findComboId, filterCards, level);
        return foxCardCombo;
    }

    /**
     * 从剩余牌库找到要的牌型
     *
     * @param comboId
     * @return
     */
    public FoxCardCombo findCombo(int comboId) {
        List<FoxHandCard> showCards = new ArrayList<>(gameData.getRemainHandCards());
        //先把石头牌拿出来
        List<FoxHandCard> resultCards = new ArrayList<>();
        for (FoxHandCard showCard : showCards) {
            if (config.isNotPointAndNotColorHandCard(showCard.getReinforceEffect())) {//石头牌
                resultCards.add(showCard);
                continue;
            }
        }
        showCards.removeAll(resultCards);

        showCards.sort((o1, o2) -> {
            int score1 = config.getPointToScoreMap().get(o1.getPoint());
            int score2 = config.getPointToScoreMap().get(o2.getPoint());
            int compare = Integer.compare(score2, score1);
            if (compare == 0) {
                compare = Integer.compare(o2.getPoint(), o1.getPoint());
            }
            return compare;
        });//按点数排序 从大到小

        //顺子可以间隔的点数,默认一点
        int straightInterval = 1;
        //哪些颜色可以视为同一花色
        Map<Integer, Set<Integer>> sameColorMap = new HashMap<>();
        sameColorMap.put(0, new HashSet<>()); //0和所有花色都是同花色
        for (int color = 1; color <= CARD_COLOR_NUM; color++) {
            Set<Integer> sameColors = new HashSet<>();
            sameColors.add(color);
            sameColorMap.put(color, sameColors);

            sameColorMap.get(0).add(color);
        }
        //顺子和花色的最小数量要求
        int straightMinNum = 5;


        switch (comboId) {
            case FoxCardProto.eFoxCardComboType.STRAIGHT_FLUSH_VALUE: {//同花顺
                if (showCards.size() < straightMinNum) {
                    break;
                }
                List<FoxHandCard> sameColorCards = config.getSameColorCards(showCards, sameColorMap, straightMinNum);
                if (sameColorCards.isEmpty()) {//没找到同花
                    break;
                }
                List<FoxHandCard> straightCard = config.getStraightCards(sameColorCards, straightInterval, straightMinNum);
                if (straightCard.isEmpty()) { //没找到顺子
                    break;
                }
                Set<FoxHandCard> findCards = new HashSet<>();//去重一下
                findCards.addAll(straightCard);
                findCards.addAll(sameColorCards);
                resultCards.addAll(findCards);
            }
            break;
            case FoxCardProto.eFoxCardComboType.STRAIGHT_VALUE: {//顺子
                if (showCards.size() < straightMinNum) {
                    break;
                }
                List<FoxHandCard> straightCard = config.getStraightCards(showCards, straightInterval, straightMinNum);
                if (straightCard.isEmpty()) { //没找到顺子
                    break;
                }
                resultCards.addAll(straightCard);
            }
            break;
            case FoxCardProto.eFoxCardComboType.FLUSH_VALUE: {//同花
                if (showCards.size() < straightMinNum) {
                    break;
                }
                List<FoxHandCard> sameColorCards = config.getSameColorCards(showCards, sameColorMap, straightMinNum);
                if (sameColorCards.isEmpty()) {//没找到同花
                    break;
                }
                resultCards.addAll(sameColorCards);
            }
            case FoxCardProto.eFoxCardComboType.FOUR_OF_A_KIND_VALUE: {//四条
                if (showCards.size() < 4) {
                    break;
                }
                Set<FoxHandCard> findCards = new HashSet<>();
                for (int i = 0; i < showCards.size() - 3; i++) {
                    FoxHandCard nowCard = showCards.get(i);
                    FoxHandCard nextCard = showCards.get(i + 1);
                    FoxHandCard nextNextCard = showCards.get(i + 2);
                    FoxHandCard nextNextNextCard = showCards.get(i + 3);
                    if (nowCard.getPoint() == nextCard.getPoint() && nowCard.getPoint() == nextNextCard.getPoint() && nowCard.getPoint() == nextNextNextCard.getPoint()) {
                        findCards.add(nowCard);
                        findCards.add(nextCard);
                        findCards.add(nextNextCard);
                        findCards.add(nextNextNextCard);
                        break;
                    }
                }
                if (findCards.isEmpty()) {
                    break;
                }
                resultCards.addAll(findCards);
            }
            break;
            case FoxCardProto.eFoxCardComboType.FULL_HOUSE_VALUE: {//葫芦
                if (showCards.size() < 5) {
                    break;
                }
                Set<FoxHandCard> findCards = new HashSet<>();
                for (int i = 0; i < showCards.size() - 2; i++) {
                    FoxHandCard nowCard = showCards.get(i);
                    FoxHandCard nextCard = showCards.get(i + 1);
                    FoxHandCard nextNextCard = showCards.get(i + 2);
                    if (nowCard.getPoint() == nextCard.getPoint() && nowCard.getPoint() == nextNextCard.getPoint()) {
                        findCards.add(nowCard);
                        findCards.add(nextCard);
                        findCards.add(nextNextCard);
                        break;
                    }
                }
                if (findCards.isEmpty()) {
                    break;
                }
                for (int i = 0; i < showCards.size() - 1; i++) {
                    FoxHandCard nowCard = showCards.get(i);
                    FoxHandCard nextCard = showCards.get(i + 1);
                    if (nowCard.getPoint() == nextCard.getPoint() && !findCards.contains(nowCard)) {
                        findCards.add(nowCard);
                        findCards.add(nextCard);
                        break;
                    }
                }
                if (findCards.size() < 5) {
                    break;
                }
                resultCards.addAll(findCards);
            }
            break;
            case FoxCardProto.eFoxCardComboType.THREE_OF_A_KIND_VALUE: {//三条
                if (showCards.size() < 3) {
                    break;
                }
                Set<FoxHandCard> findCards = new HashSet<>();
                for (int i = 0; i < showCards.size() - 2; i++) {
                    FoxHandCard nowCard = showCards.get(i);
                    FoxHandCard nextCard = showCards.get(i + 1);
                    FoxHandCard nextNextCard = showCards.get(i + 2);
                    if (nowCard.getPoint() == nextCard.getPoint() && nowCard.getPoint() == nextNextCard.getPoint()) {
                        findCards.add(nowCard);
                        findCards.add(nextCard);
                        findCards.add(nextNextCard);
                        break;
                    }
                }
                if (findCards.isEmpty()) {
                    break;
                }
                resultCards.addAll(findCards);
            }
            break;
            case FoxCardProto.eFoxCardComboType.TWO_PAIR_VALUE: {//两对
                if (showCards.size() < 4) {
                    break;
                }
                Set<FoxHandCard> findCards = new HashSet<>();
                for (int i = 0; i < showCards.size() - 1; i++) {
                    FoxHandCard nowCard = showCards.get(i);
                    FoxHandCard nextCard = showCards.get(i + 1);
                    if (nowCard.getPoint() == nextCard.getPoint()) {
                        findCards.add(nowCard);
                        findCards.add(nextCard);
                        break;
                    }
                }
                if (findCards.size() < 2) {
                    break;
                }
                for (int i = 0; i < showCards.size() - 1; i++) {
                    FoxHandCard nowCard = showCards.get(i);
                    FoxHandCard nextCard = showCards.get(i + 1);
                    if (nowCard.getPoint() == nextCard.getPoint() && !findCards.contains(nowCard)) {
                        findCards.add(nowCard);
                        findCards.add(nextCard);
                        break;
                    }
                }
                if (findCards.size() < 4) {
                    break;
                }
                resultCards.addAll(findCards);
            }
            break;
            case FoxCardProto.eFoxCardComboType.ONE_PAIR_VALUE: {//一对
                if (showCards.size() < 2) {
                    break;
                }
                Set<FoxHandCard> findCards = new HashSet<>();
                for (int i = 0; i < showCards.size() - 1; i++) {
                    FoxHandCard nowCard = showCards.get(i);
                    FoxHandCard nextCard = showCards.get(i + 1);
                    if (nowCard.getPoint() == nextCard.getPoint()) {
                        findCards.add(nowCard);
                        findCards.add(nextCard);
                        break;
                    }
                }
                if (findCards.isEmpty()) {
                    break;
                }
                resultCards.addAll(findCards);
            }
            break;
            case FoxCardProto.eFoxCardComboType.HIGH_CARD_VALUE: {//散牌
                if (showCards.size() < 1) {
                    break;
                }
                resultCards.add(showCards.get(0));
            }
        }
        if (resultCards.isEmpty()) {//找不到要的牌型
            return null;
        }

        FoxCardCombo foxCardCombo = new FoxCardCombo(comboId, resultCards, 1);
        return foxCardCombo;
    }

    public void refreshShopPos1SaleCards() {
        //先把标签效果走一遍
        dealLabel(eFoxCardTriggerType.NEW_SALE_CARD);

        int saleCardNum = getShopSaleCardNum();
        List<FoxSaleCard> saleCards = new ArrayList<>();
        for (int i = 0; i < saleCardNum; i++) {
            Set<Integer> poolQualityHave = calcShopEffectCardPoolQuality(); //小丑牌卡池中有的品质
            Map<Integer, Integer> typeWeightMap = calcShopIdWeightFromPos1(!poolQualityHave.isEmpty());//类型权重
            int type = config.getRandomHelper().getRandomKeyByWeightEx(typeWeightMap);
            FoxSaleCard saleCard = new FoxSaleCard();
            saleCard.setId(gameData.generalCardUniqueId());
            saleCard.setType(type);
            if (type == eFoxCardType.EFFECT.getValue()) {//随机到小丑牌,继续随机品质
//                如果随机到小丑牌，再进行品质随机，例如普通40%、优秀40%、传奇15%、无双5%
//                        1	如果玩家拥有【传奇赐福】或【无双赐福】，则小丑牌出现概率增加到100%，并且小丑牌是传奇或无双的概率本次提升至100%。
//
//                完成品质随机后，再从对应的品质库中，随机一个对应品质的小丑牌。
//                1	如果该品质库中已经没有小丑牌可随机。则本次抽奖替换为扑克牌，并按照扑克牌的刷新规则进行刷新。
//
//                完成小丑牌的抽取后，再进行版本随机，例如10%概率为其添加版本，90%概率保持不变。
//                1	确定添加版本后，再根据闪亮、镭射、多彩、负片的配置概率进行随机。
//                2	如果玩家拥有【闪亮赐福】或【负片赐福】这类版本赐福，那么添加版本的概率本次提升至100%，并且必定添加对应赐福的版本。
                int quality = config.randomShopEffectCardQuality(poolQualityHave);
                FoxCardEffectCardConfig effectCardConfig = randomPopShopEffectCardPool(quality);
                saleCard.setConfigId(effectCardConfig.getId());
                int versionEffect = getSaleEffectCardVersionEffect();
                saleCard.setVersionEffect(versionEffect);
                int price = config.calcEffectCardPrice(effectCardConfig, versionEffect);
                saleCard.setPrice(price);
            } else if (type == eFoxCardType.HAND.getValue()) {//随机到扑克牌,继续随机品质
//                如果随机到扑克牌，先进行花色 + 点数的随机。再进行3次版本效果随机：
//                进行增强效果随机，例20%可获得增强效果，80%无法获得增强效果。
//                1	如果确定获得了增强效果，再根据8种增强效果的权重进行随机。
//                进行版本效果随机，例5%可获得版本效果，95%无法获得版本效果。
//                1	如果确定获得了版本效果，再根据闪亮、镭射、多彩的权重进行版本效果随机
//                进行印章效果随机，例10%可获得印章效果。90%无法获得印章效果。
//                1	如果确定获得了印章效果，再根据4种印章的权重进行随机。
//                以上提到的增强、版本、印章效果随机均独立进行，一张扑克牌允许同时随机到3种效果。
                saleCard = randomSaleHandCard();
            } else if (type == eFoxCardType.MAGIC_WEAPON.getValue()) {//随机到法宝(星球牌
                FoxCardMagicWeaponConfig weaponConfig = config.randomMagicWeapon();
                saleCard.setConfigId(weaponConfig.getId());
                saleCard.setPrice(weaponConfig.getGold());
            } else if (type == eFoxCardType.SPELL.getValue()) {//随机到塔罗牌
                FoxCardSpellConfig spellConfig = config.randomSpell();
                saleCard.setConfigId(spellConfig.getId());
                saleCard.setPrice(spellConfig.getGold());
            } else if (type == eFoxCardType.SORCERY.getValue()) {//随机到幻灵牌
                FoxCardSorceryConfig sorceryConfig = config.randomSorcery();
                saleCard.setConfigId(sorceryConfig.getId());
                saleCard.setPrice(sorceryConfig.getGold());
            } else {
                continue;
            }

            saleCards.add(saleCard);
        }
        gameData.getShop().getSaleCards().addAll(saleCards);
        gameData.getShop().setUpdateOption();
    }

    /**
     * 初始化商店数据,刷新商品
     */
    private void gameShopInit(List<Integer> equipCards, List<Integer> unlockCards) {
        gameData.setRoundLimitEffectCard(new HashSet<>());
        resetRemainHandCards();//重置手牌库
        //第一轮或者BOSS后的那一轮,才刷新优惠券
        boolean isRefreshCoupon = false;
        int lastRound = gameData.getRound();
        FoxCardRoundConfig roundConfig = config.getRoundConfigMap().get(lastRound);
        if (roundConfig != null && roundConfig.getIsLimit() == 1) {
            isRefreshCoupon = true;
        }

        UserFoxCardShop shop = gameData.getShop();
        shop.getSaleCards().clear();
        shop.getCardPacks().clear();
        shop.setRefreshAmount(0);
        shop.setRefreshPrice(config.getShopRefreshParams().get(0));
        //小丑牌的卡池重新抽
        Set<Integer> inPoolCardIds = new HashSet<>();
        for (Integer cardId : equipCards) {
            unlockCards.remove(cardId);
        }
        Collections.shuffle(unlockCards);
        for (int i = 0; i < config.getShopEffectCardPoolNum() - equipCards.size() && i < unlockCards.size(); i++) {
            inPoolCardIds.add(unlockCards.get(i));
        }
        inPoolCardIds.addAll(equipCards);
        shop.setInPoolCardIds(new ArrayList<>(inPoolCardIds)); //设置可刷新到的效果牌
        //随机销售牌
        gameData.getShop().getSaleCards().clear();
        refreshShopPos1SaleCards();

        //优惠券
        if (isRefreshCoupon) {
            FoxCardCouponConfig couponConfig = config.randomCoupon(new HashSet<>(gameData.getCoupons()));
            if (couponConfig != null) {
                FoxSaleCard coupon = new FoxSaleCard();
                coupon.setId(gameData.generalCardUniqueId());
                coupon.setConfigId(couponConfig.getId());
                coupon.setPrice(couponConfig.getGold());
                coupon.setType(eFoxCardType.COUPON.getValue());
                shop.setCoupon(coupon);
            } else {
                shop.setCoupon(null);
            }
        }

        //卡包
        List<FoxCardShopConfig> packConfigList = config.randomShopSalePack();
        for (FoxCardShopConfig shopConfig : packConfigList) {
            FoxCardPack pack = new FoxCardPack();
            pack.setCardPackId(shopConfig.getId());
            pack.setPackType(shopConfig.getCardType());
            pack.setPrice(shopConfig.getGold());
            shop.getCardPacks().add(pack);
        }

        dealLabel(eFoxCardTriggerType.SHOP_INIT);

        dealCardEffect(eFoxCardTriggerType.SHOP_INIT);

        dealCoupon4();

        gameData.getShop().setUpdateOption();
    }

    /**
     * 每当进行商店货物刷新时，刷新所需的金币价格-
     */
    public void dealCoupon4() {
        int refreshPrice = gameData.getShop().getRefreshPrice();
        for (Integer coupon : gameData.getCoupons()) {
            FoxCardCouponConfig couponConfig = config.getCouponConfigMap().get(coupon);
            if (couponConfig != null && couponConfig.getType() == eFoxCardCouponType.COUPON4.getValue()) {//降低刷新的商品价格
                int decPrice = couponConfig.getParamList().get(1);
                refreshPrice -= decPrice;
            }
        }
        if (refreshPrice < 0) {
            refreshPrice = 0;
        }
        gameData.getShop().setRefreshPrice(refreshPrice);
    }

    /**
     * 优惠券会影响商店的销售牌数量
     *
     * @return
     */
    public int getShopSaleCardNum() {
        int saleCardNum = config.getShopSaleCardNum();
        for (Integer couponId : gameData.getCoupons()) {
            FoxCardCouponConfig couponConfig = config.getCouponConfigMap().get(couponId);
            if (couponConfig != null && couponConfig.getType() == eFoxCardCouponType.COUPON1.getValue() && couponConfig.getParamList().get(0) == 1) {
                saleCardNum += couponConfig.getParamList().get(1);
            }
        }

        return saleCardNum - gameData.getShop().getSaleCards().size();//要减去已经有的销售牌数量
    }

    /**
     * 计算商店销售牌的类型权重
     *
     * @return
     */
    private Map<Integer, Integer> calcShopIdWeightFromPos1(boolean isNeedEffectCard) {
        int totalWeight = 1000;

        Map<Integer, Integer> typeWeightMap = new HashMap<>();
        for (FoxCardShopConfig shopConfig : config.getShopConfigMap().values()) {
            if (shopConfig.getPosition() != 1) {
                continue;
            }
            if (shopConfig.getCardType() != eFoxCardType.EFFECT.getValue()) {//小丑牌的权重等于其他权重剩下的
                int shopIdWeight = getShopTypeWeight(shopConfig);
                typeWeightMap.put(shopConfig.getCardType(), shopIdWeight);
                totalWeight -= shopIdWeight;
            }
        }
        if (isNeedEffectCard) {//需要小丑牌,小丑牌被刷完了就不需要小丑牌
            typeWeightMap.put(eFoxCardType.EFFECT.getValue(), totalWeight);
        }
        return typeWeightMap;
    }

    private int getShopTypeWeight(FoxCardShopConfig shopConfig) {
        int weight = shopConfig.getWeight();
        for (Integer couponId : gameData.getCoupons()) {
            FoxCardCouponConfig couponConfig = config.getCouponConfigMap().get(couponId);
            //拥有优惠券后，商店刷新时，出现{0}牌的概率*{1}
            if (couponConfig != null && couponConfig.getType() == eFoxCardCouponType.COUPON7.getValue() && couponConfig.getParamList().get(0) == shopConfig.getCardType()) {
                Integer multi = couponConfig.getParamList().get(1);
                weight = BigDecimal.valueOf(weight).multiply(BigDecimal.valueOf(multi + 1000).divide(BigDecimal.valueOf(1000))).intValue();
            }
        }
        return weight;
    }

    /**
     * 商店池里有的小丑牌品质
     *
     * @return
     */
    public Set<Integer> calcShopEffectCardPoolQuality() {
        List<Integer> poolIds = gameData.getShop().getInPoolCardIds();
        Set<Integer> qualityList = new HashSet<>();
        for (Integer cardId : poolIds) {
            FoxCardEffectCardConfig effectCardConfig = config.getEffectCardConfigMap().get(cardId);
            if (effectCardConfig == null) {
                continue;
            }
            if (gameData.getEffectCards().stream().anyMatch(card -> card.getConfigId() == effectCardConfig.getId())) {//去重
                continue;
            }
            qualityList.add(effectCardConfig.getQuality());
        }
        return qualityList;
    }

    /**
     * 从商店卡池里随机一张卡,随机到后挪出卡池
     *
     * @param quality
     * @return
     */
    public FoxCardEffectCardConfig randomPopShopEffectCardPool(int quality) {
        List<Integer> poolIds = gameData.getShop().getInPoolCardIds();
        List<FoxCardEffectCardConfig> cards = new ArrayList<>();
        for (Integer cardId : poolIds) {
            FoxCardEffectCardConfig effectCardConfig = config.getEffectCardConfigMap().get(cardId);
            if (effectCardConfig == null) {
                continue;
            }
            if (gameData.getEffectCards().stream().anyMatch(card -> card.getConfigId() == effectCardConfig.getId())) {//去重
                continue;
            }
            if (effectCardConfig.getQuality() == quality) {
                cards.add(effectCardConfig);
            }
        }
        if (cards.isEmpty()) {
            return null;
        }
        FoxCardEffectCardConfig effectCardConfig = RandomHelper.getRandomList(cards, 1).get(0);

        for (int i = 0; i < gameData.getShop().getInPoolCardIds().size(); i++) {
            if (gameData.getShop().getInPoolCardIds().get(i) == effectCardConfig.getId()) {
                gameData.getShop().getInPoolCardIds().remove(i);
                gameData.getShop().getOutPoolCardIds().add(effectCardConfig.getId());
                break;
            }
        }
        gameData.getShop().setUpdateOption();
        return effectCardConfig;
    }

    /**
     * 处理等待选择的牌
     *
     * @param chooseId
     * @param effectTargetIdList
     */
    public boolean dealWaitChooseCard(int chooseId, List<Integer> effectTargetIdList, boolean isSkip) {
        if (isSkip) {
            gameData.setRemainChooseTimes(0);
            gameData.getWaitChooseCards().clear();
            resetRemainHandCards();//重置牌库

            dealCardEffect(eFoxCardTriggerType.SKIP_PACK_CHOOSE);
            return true;
        }
        FoxSaleCard saleCard = gameData.getWaitChooseCards().stream().filter(card -> card.getId() == chooseId).findFirst().orElse(null);
        if (saleCard == null) {
            return false;
        }
        if (saleCard.getType() == eFoxCardType.EFFECT.getValue()) {
            addNewEffectCard(saleCard.getConfigId(), saleCard.getVersionEffect(), saleCard.getId());
        } else if (saleCard.getType() == eFoxCardType.HAND.getValue()) {
            addNewHandCard(saleCard.getPoint(), saleCard.getColor(), saleCard.getReinforceEffect(), saleCard.getVersionEffect(), saleCard.getSealEffect(), saleCard.getId());
        } else if (saleCard.getType() == eFoxCardType.MAGIC_WEAPON.getValue()) {
            magicWeaponUse(saleCard.getConfigId());
        } else if (saleCard.getType() == eFoxCardType.SPELL.getValue()) {
            if (!spellUse(saleCard.getConfigId(), effectTargetIdList, null)) {
                return false;
            }
        } else if (saleCard.getType() == eFoxCardType.SORCERY.getValue()) {
            if (!sorceryUse(saleCard.getConfigId(), effectTargetIdList, null)) {
                return false;
            }
        }

        gameData.getWaitChooseCards().remove(saleCard);
        gameData.setRemainChooseTimes(gameData.getRemainChooseTimes() - 1);
        if (gameData.getRemainChooseTimes() <= 0) {
            gameData.getWaitChooseCards().clear();
            resetRemainHandCards();//重置牌库
        }
        return true;
    }

    /**
     * 重置牌库
     */
    public void resetRemainHandCards() {
        List<FoxHandCard> list = new ArrayList<>();
        list.addAll(gameData.getHandCards());
        list.addAll(gameData.getRemainHandCards());
        list.addAll(gameData.getDropHandCards());

        gameData.setRemainHandCards(list);
        gameData.getHandCards().clear();
        gameData.getDropHandCards().clear();
    }

    private boolean sorceryUse(int configId, List<Integer> effectTargetIdList, FoxConsumeCard consumeCard) {
        FoxCardSorceryConfig sorceryConfig = config.getSorceryConfigMap().get(configId);
        if (sorceryConfig == null) {
            return false;
        }
        try {
            FoxCardSorceryUse use = getCardInterfaceInstance("FoxCardSorceryUse" + sorceryConfig.getType());
            return use.init(sorceryConfig, effectTargetIdList, consumeCard).use(this);
        } catch (Exception e) {
            logger.error("取不到幻灵牌的实现 {}={} err:{}", configId, sorceryConfig.getType(), e);
        }
        return false;
    }

    private boolean spellUse(int configId, List<Integer> effectTargetIdList, FoxConsumeCard consumeCard) {
        FoxCardSpellConfig spellConfig = config.getSpellConfigMap().get(configId);
        if (spellConfig == null) {
            return false;
        }
        try {
            FoxCardSpellUse use = getCardInterfaceInstance("FoxCardSpellUse" + spellConfig.getType());
            return use.init(spellConfig, effectTargetIdList, consumeCard).use(this);
        } catch (Exception e) {
            logger.error("取不到塔罗牌的实现 {}={} err:{}", configId, spellConfig.getType(), e);
        }
        return false;
    }

    public boolean magicWeaponUse(FoxConsumeCard consumeCard) {
        destroyConsumeCards(Collections.singletonList(consumeCard));

        return magicWeaponUse(consumeCard.getConfigId());
    }

    public boolean magicWeaponUse(int configId) {
        Integer times = 1;
        for (Integer coupon : gameData.getCoupons()) {
            FoxCardCouponConfig couponConfig = config.getCouponConfigMap().get(coupon);
            if (couponConfig != null && couponConfig.getType() == eFoxCardCouponType.COUPON6.getValue()) {//拥有优惠券后，使用星球牌时，提升的牌型等级额外+{1}
                times+= couponConfig.getParamList().get(1);
            }
        }
        comboLevelUp(configId, times);

        Integer useTimes = gameData.getConsumeCardTimesMap().getOrDefault(eFoxCardType.MAGIC_WEAPON.getValue(), 0);
        gameData.getConsumeCardTimesMap().put(eFoxCardType.MAGIC_WEAPON.getValue(), useTimes + 1);
        gameData.setLastUsedConsumeCardId(configId * 100 + eFoxCardType.MAGIC_WEAPON.getValue());
        setLastUseConsumeType(eFoxCardType.MAGIC_WEAPON.getValue());

        return true;
    }

    /**
     * 牌型等级提升
     *
     * @param configId
     */
    public FoxCardShowInfo comboLevelUp(int configId, int num) {
        FoxCardMagicWeaponConfig weaponConfig = config.getMagicWeaponConfigMap().get(configId);
        if (weaponConfig == null) {
            return null;
        }
        List<Integer> list = gameData.getComboInfoMap().get(configId);
        if (list == null) {
            list = new ArrayList<>();
            list.add(0);
            list.add(0);
            gameData.getComboInfoMap().put(configId, list);
        }
        int level = list.get(0);
        level += num;
        list.set(0, level);

        FoxCardShowInfo showInfo = newFoxShow(eFoxCardShowType.COMBO_LV_UP.getValue());
        showInfo.setShowValue(String.valueOf(configId));
        return showInfo;
    }

    public FoxCardShowInfo addNewHandCard(int point, int color, int reinforceEffect, int versionEffect, int sealEffect) {
        return addNewHandCard(point, color, reinforceEffect, versionEffect, sealEffect, gameData.generalCardUniqueId());
    }

    public FoxCardShowInfo addNewHandCard(int point, int color, int reinforceEffect, int versionEffect, int sealEffect, int uniqueId) {
        FoxHandCard handCard = new FoxHandCard(uniqueId, eFoxCardType.HAND.getValue(), 0, point, color);
        handCard.setReinforceEffect(reinforceEffect);
        handCard.setVersionEffect(versionEffect);
        handCard.setSealEffect(sealEffect);
        handCard.setPrice(config.calcHandCardPrice(reinforceEffect, versionEffect, sealEffect));
        gameData.getHandCards().add(handCard);

        dealHandCardBuff(handCard, eFoxCardTriggerType.ADD);

        dealCardEffect(eFoxCardTriggerType.ADD, handCard);

        FoxCardShowInfo showInfo = newFoxShow(eFoxCardShowType.ADD_CARD.getValue());
        showInfo.setSettleCards(Collections.singletonList(handCard));
        return showInfo;
    }

    public FoxCardShowInfo addNewEffectCard(int configId, int versionEffect) {
        return addNewEffectCard(configId, versionEffect, gameData.generalCardUniqueId());
    }

    public FoxCardShowInfo addNewEffectCard(int configId, int versionEffect, int uniqueId) {
        FoxCardEffectCardConfig effectCardConfig = config.getEffectCardConfigMap().get(configId);
        if (effectCardConfig == null) {
            return null;
        }
        int price = config.calcEffectCardPrice(effectCardConfig, versionEffect);
        FoxEffectCard effectCard = new FoxEffectCard(uniqueId, eFoxCardType.EFFECT.getValue(), price, configId);
        effectCard.setVersionEffect(versionEffect);
        gameData.getEffectCards().add(effectCard);

        dealCardEffect(effectCard, eFoxCardTriggerType.ADD);
        dealEffectCardBuff(effectCard, eFoxCardTriggerType.ADD);

        FoxCardShowInfo showInfo = newFoxShow(eFoxCardShowType.ADD_CARD.getValue());
        showInfo.setSettleCards(Collections.singletonList(effectCard));

        for (int i = 0; i < gameData.getShop().getInPoolCardIds().size(); i++) {
            Integer cardId = gameData.getShop().getInPoolCardIds().get(i);
            if (effectCard.getConfigId() == cardId) {
                gameData.getShop().getInPoolCardIds().remove(i);
                gameData.getShop().getOutPoolCardIds().add(cardId);
                break;
            }
        }
        return showInfo;
    }

    public FoxCardShowInfo addNewConsumeCard(int cardType, int configId, int versionEffect) {
        return addNewConsumeCard(cardType, configId, versionEffect, gameData.generalCardUniqueId());
    }

    public FoxCardShowInfo addNewConsumeCard(int cardType, int configId, int versionEffect, int uniqueId) {
        int price = config.calcConsumeCardPrice(cardType, configId, versionEffect);
        FoxConsumeCard consumeCard = new FoxConsumeCard(uniqueId, cardType, price, configId);
        consumeCard.setVersionEffect(versionEffect);
        gameData.getConsumeCards().add(consumeCard);

        dealConsumeCardBuff(consumeCard, eFoxCardTriggerType.ADD);

        FoxCardShowInfo showInfo = newFoxShow(eFoxCardShowType.ADD_CARD.getValue());
        showInfo.setSettleCards(Collections.singletonList(consumeCard));
        return showInfo;
    }

    public FoxBaseCard saleCard(int cardId) {
        FoxBaseCard baseCard = null;
        baseCard = gameData.getEffectCards().stream().filter(card -> card.getId() == cardId).findFirst().orElse(null);
        if (baseCard == null) {
            baseCard = gameData.getConsumeCards().stream().filter(card -> card.getId() == cardId).findFirst().orElse(null);
        }
        if (baseCard != null) {
            int price = baseCard.getPrice();
            int goldAdd = BigDecimal.valueOf(price).multiply(BigDecimal.valueOf(config.getSalePriceOff()).divide(BigDecimal.valueOf(1000))).intValue();
            //最低1金币
            if (goldAdd < 1) {
                goldAdd = 1;
            }

            if (baseCard instanceof FoxEffectCard) {
                destroyEffectCards(Collections.singletonList((FoxEffectCard) baseCard));

                FoxCardLimitConfig limitConfig = config.getLimitConfigMap().get(gameData.getBossLimitEffect());
                if (limitConfig != null && limitConfig.getType() == eFoxCardLimitType.LIMIT7.getValue()) {//有这个限制扑克牌不计分
                    gameData.setBossLimitEffect(0);//卖出任意小丑牌,这个限制消失
                }
            } else if (baseCard instanceof FoxConsumeCard) {
                destroyConsumeCards(Collections.singletonList((FoxConsumeCard) baseCard));
            }

            goldChange(goldAdd, null, null, eFoxCardTriggerType.SALE);

            dealCardEffect(eFoxCardTriggerType.SALE);

            return baseCard;
        }
        return null;
    }

    /**
     * 获得小丑卡的版本效果,有标签就用掉标签,没有就随机
     *
     * @return
     */
    public int getSaleEffectCardVersionEffect() {
        for (Integer labelId : gameData.getSkipLabelHaves()) {
            int version = dealLabel(labelId, eFoxCardTriggerType.NEW_SALE_EFFECT_CARD);
            if (version != 0) {
                return version;
            }
        }
        return config.randomVersionEffect(eFoxCardType.EFFECT.getValue(), getCouponAddValue(eFoxCardCouponType.COUPON8.getValue()));
    }

    public void addShopSaleCard(FoxSaleCard saleCard) {
        gameData.getShop().getSaleCards().add(saleCard);

        if (saleCard.getType() == eFoxCardType.EFFECT.getValue()) {
            for (int i = 0; i < gameData.getShop().getInPoolCardIds().size(); i++) {
                Integer cardId = gameData.getShop().getInPoolCardIds().get(i);
                if (saleCard.getConfigId() == cardId) {
                    gameData.getShop().getInPoolCardIds().remove(i);
                    gameData.getShop().getOutPoolCardIds().add(cardId);
                    break;
                }
            }
        }
    }

    /**
     * 购买卡片
     *
     * @param buyId
     * @param posType 1 可刷新的卡 2 卡包 3 优惠券
     * @param isUse   是否购买并使用
     * @return
     */
    public boolean buyCard(int buyId, int posType, boolean isUse) {
        int price = calcBuyPrice(buyId, posType);
        if (gameData.getGold() < price) {
            return false;
        }

        if (posType == 1) {//刷新的卡
            FoxSaleCard foxSaleCard = gameData.getShop().getSaleCards().stream().filter(card -> card.getId() == buyId).findFirst().orElse(null);
            if (foxSaleCard.getType() == eFoxCardType.EFFECT.getValue()) {
                addNewEffectCard(foxSaleCard.getConfigId(), foxSaleCard.getVersionEffect(), foxSaleCard.getId());
            } else if (foxSaleCard.getType() == eFoxCardType.MAGIC_WEAPON.getValue()) {
                if (!isUse) {
                    addNewConsumeCard(foxSaleCard.getType(), foxSaleCard.getConfigId(), foxSaleCard.getVersionEffect(), foxSaleCard.getId());
                } else {
                    magicWeaponUse(foxSaleCard.getConfigId());
                }
            } else if (foxSaleCard.getType() == eFoxCardType.SPELL.getValue()) {
                if (!isUse) {
                    addNewConsumeCard(foxSaleCard.getType(), foxSaleCard.getConfigId(), foxSaleCard.getVersionEffect(), foxSaleCard.getId());
                } else {//加入待处理,发手牌
                    if (!spellUse(foxSaleCard.getConfigId(), new ArrayList<>(), null)) {
                        return false;
                    }
                }
            } else if (foxSaleCard.getType() == eFoxCardType.SORCERY.getValue()) {
                if (!isUse) {
                    addNewConsumeCard(foxSaleCard.getType(), foxSaleCard.getConfigId(), foxSaleCard.getVersionEffect(), foxSaleCard.getId());
                } else {//加入待处理,发手牌
                    if(!sorceryUse(foxSaleCard.getConfigId(), new ArrayList<>(), null)) {
                        return false;
                    }
                }
            } else if (foxSaleCard.getType() == eFoxCardType.HAND.getValue()) {
                addNewHandCard(foxSaleCard.getPoint(), foxSaleCard.getColor(), foxSaleCard.getReinforceEffect(), foxSaleCard.getVersionEffect(), foxSaleCard.getSealEffect(), foxSaleCard.getId());
                resetRemainHandCards();
            }

            gameData.getShop().getSaleCards().remove(foxSaleCard);
        } else if (posType == 2) {//卡包
            FoxCardPack pack = gameData.getShop().getCardPacks().stream().filter(card -> card.getCardPackId() == buyId).findFirst().orElse(null);
            if (pack == null) {
                return false;
            }
            openCardPack(pack.getCardPackId());
            gameData.getShop().getCardPacks().remove(pack);
        } else if (posType == 3) {//优惠券
            FoxSaleCard coupon = gameData.getShop().getCoupon();
            if (coupon == null) {
                return false;
            }
            gameData.getShop().setCoupon(null);
            gameData.getShop().setUpdateOption();
            gameData.getCoupons().add(coupon.getConfigId());

            FoxCardCouponConfig couponConfig = config.getCouponConfigMap().get(coupon.getConfigId());
            if (couponConfig != null) {
                if (couponConfig.getType() == eFoxCardCouponType.COUPON1.getValue()) {
                    if (couponConfig.getParamList().get(0) == 1) {
                        //增加售卖的卡牌，要立马加
                        refreshShopPos1SaleCards();
                    } else if (couponConfig.getParamList().get(0) == 2) {//加消耗牌持有上限
                        gameData.setConsumeCardNumLimit(gameData.getConsumeCardNumLimit() + couponConfig.getParamList().get(1));
                    } else if (couponConfig.getParamList().get(0) == 3) {//加小丑牌持有上限
                        gameData.setEffectCardNumLimit(gameData.getEffectCardNumLimit() + couponConfig.getParamList().get(1));
                    }
                } else if (couponConfig.getType() == eFoxCardCouponType.COUPON2.getValue()) {
                    reCalcCardOpTimes();
                } else if (couponConfig.getType() == eFoxCardCouponType.COUPON4.getValue()) {
                    dealCoupon4();
                }
            }
        }

        goldChange(-price, null, null, eFoxCardTriggerType.SHOP_BUY);

        return true;
    }

    public void openCardPack(int packId) {
        FoxCardShopConfig shopConfig = config.getShopConfigMap().get(packId);

        int cardNum = shopConfig.getParamList().get(0);
        int chooseNum = shopConfig.getParamList().get(1);

        gameData.setRemainChooseTimes(chooseNum);

        List<FoxSaleCard> saleCards = new ArrayList<>();
        if (shopConfig.getCardType() == eFoxCardType.EFFECT.getValue()) {

            List<FoxSaleCard> foxSaleCards = config.randomEffectCard(gameData.getEffectCards(), 0, cardNum, -1, getCouponAddValue(eFoxCardCouponType.COUPON8.getValue()));

            for (FoxSaleCard saleCard : foxSaleCards) {
                saleCard.setId(gameData.generalCardUniqueId());
                saleCards.add(saleCard);
            }
        } else if (shopConfig.getCardType() == eFoxCardType.HAND.getValue()) {
            for (int i = 0; i < cardNum; i++) {
                FoxSaleCard saleCard = randomSaleHandCard();
                saleCards.add(saleCard);
            }
        } else if (shopConfig.getCardType() == eFoxCardType.MAGIC_WEAPON.getValue()) {
            List<FoxCardMagicWeaponConfig> weaponConfigList = new ArrayList<>();
            //拥有优惠券后，使用天体包时，随机的星球牌必定有一张，是你等级最高的牌型。
            HashSet<Integer> excludeIds = new HashSet<>();
            if (haveCouponByType(eFoxCardCouponType.COUPON5.getValue())) {
                int maxLevelComboId = getMaxLevelComboId();
                FoxCardMagicWeaponConfig weaponConfig = config.getMagicWeaponConfigMap().get(maxLevelComboId);
                weaponConfigList.add(weaponConfig);
                excludeIds.add(weaponConfig.getId());
                cardNum--;
            }
            if (cardNum > 0) {
                weaponConfigList.addAll(config.randomMagicWeapons(cardNum, excludeIds));
            }

            for (FoxCardMagicWeaponConfig weaponConfig : weaponConfigList) {
                FoxSaleCard saleCard = new FoxSaleCard();
                saleCard.setId(gameData.generalCardUniqueId());
                saleCard.setType(eFoxCardType.MAGIC_WEAPON.getValue());
                saleCard.setConfigId(weaponConfig.getId());
                saleCard.setPrice(weaponConfig.getGold());
                saleCards.add(saleCard);
            }
        } else if (shopConfig.getCardType() == eFoxCardType.SPELL.getValue()) {
            List<FoxCardSpellConfig> foxCardSpellConfigs = config.randomSpells(cardNum);
            for (FoxCardSpellConfig spellConfig : foxCardSpellConfigs) {
                FoxSaleCard saleCard = new FoxSaleCard();
                saleCard.setId(gameData.generalCardUniqueId());
                saleCard.setType(eFoxCardType.SPELL.getValue());
                saleCard.setConfigId(spellConfig.getId());
                saleCard.setPrice(spellConfig.getGold());
                saleCards.add(saleCard);
            }
            addHandCards();
        } else if (shopConfig.getCardType() == eFoxCardType.SORCERY.getValue()) {
            List<FoxCardSorceryConfig> foxCardSorceryConfigs = config.randomSorceryList(cardNum);
            for (FoxCardSorceryConfig sorceryConfig : foxCardSorceryConfigs) {
                FoxSaleCard saleCard = new FoxSaleCard();
                saleCard.setId(gameData.generalCardUniqueId());
                saleCard.setType(eFoxCardType.SORCERY.getValue());
                saleCard.setConfigId(sorceryConfig.getId());
                saleCard.setPrice(sorceryConfig.getGold());
                saleCards.add(saleCard);
            }
            addHandCards();
        }

        gameData.setWaitChooseCards(saleCards);
    }

    public boolean haveCouponByType(int couponType) {
        for (Integer coupon : gameData.getCoupons()) {
            FoxCardCouponConfig couponConfig = config.getCouponConfigMap().get(coupon);
            if (couponConfig != null && couponConfig.getType() == couponType) {
                return true;
            }
        }
        return false;
    }

    /**
     * 随机生成一张卖的扑克牌
     *
     * @return
     */
    public FoxSaleCard randomSaleHandCard() {
        FoxSaleCard saleCard = new FoxSaleCard();
        saleCard.setId(gameData.generalCardUniqueId());
        saleCard.setType(eFoxCardType.HAND.getValue());
        int point = config.getRandomHelper().next(1, config.getPointToScoreMap().size() + 1);
        int color = config.getRandomHelper().next(1, CARD_COLOR_NUM + 1);
        saleCard.setPoint(point);
        saleCard.setColor(color);

        int reinforceEffect = config.randomReinforceEffect(eFoxCardType.HAND.getValue());
        saleCard.setReinforceEffect(reinforceEffect);
        int versionEffect = config.randomVersionEffect(eFoxCardType.HAND.getValue(), getCouponAddValue(eFoxCardCouponType.COUPON8.getValue()));
        saleCard.setVersionEffect(versionEffect);
        int sealEffect = config.randomSealEffect(eFoxCardType.HAND.getValue());
        saleCard.setSealEffect(sealEffect);
        int price = config.calcHandCardPrice(reinforceEffect, versionEffect, sealEffect);
        saleCard.setPrice(price);

        return saleCard;
    }

    public int calcBuyPrice(int buyId, int posType) {
        int price = 0;
        if (posType == 1) {
            FoxSaleCard foxSaleCard = gameData.getShop().getSaleCards().stream().filter(card -> card.getId() == buyId).findFirst().orElse(null);
            price = foxSaleCard.getPrice();
        } else if (posType == 2) {
            FoxCardPack pack = gameData.getShop().getCardPacks().stream().filter(card -> card.getCardPackId() == buyId).findFirst().orElse(null);
            price = pack.getPrice();
        } else if (posType == 3) {
            FoxSaleCard coupon = gameData.getShop().getCoupon();
            price = coupon.getPrice();
        }

        int multiRemain = 1000;
        for (Integer coupon : gameData.getCoupons()) {
            FoxCardCouponConfig couponConfig = config.getCouponConfigMap().get(coupon);
            if (couponConfig != null && couponConfig.getType() == eFoxCardCouponType.COUPON3.getValue()) {//在商店进行购物时，所有物品价格降低{0},向上取整
                multiRemain -= couponConfig.getParamList().get(1);
            }
        }
        if (multiRemain <= 0) {
            price = 0;
        } else {
            price = BigDecimal.valueOf(price).multiply(BigDecimal.valueOf(multiRemain).divide(BigDecimal.valueOf(1000))).setScale(0, BigDecimal.ROUND_UP).intValue();
        }
        return price;
    }

    public void refreshShop() {
        int refreshGold = gameData.getShop().getRefreshPrice();
        if (gameData.getShop().getFreeTimes() > 0) {
            refreshGold = 0;
            gameData.getShop().setFreeTimes(gameData.getShop().getFreeTimes() - 1);
        } else {
            gameData.getShop().setRefreshAmount(gameData.getShop().getRefreshAmount() + 1);
            gameData.getShop().setRefreshPrice(gameData.getShop().getRefreshPrice() + config.getShopRefreshParams().get(1));//价格增加
            gameData.getShop().setUpdateOption();
        }
        goldChange(-refreshGold, null, null, eFoxCardTriggerType.REFRESH_SHOP);

        gameData.getShop().getSaleCards().clear();
        refreshShopPos1SaleCards();

        dealCardEffect(eFoxCardTriggerType.REFRESH_SHOP);
    }

    public boolean useConsumeCard(int cardId, List<Integer> targetIdList) {
        FoxConsumeCard consumeCard = gameData.getConsumeCards().stream().filter(card -> card.getId() == cardId).findFirst().orElse(null);
        if (consumeCard == null) {
            return false;
        }

        if (consumeCard.getCardType() == eFoxCardType.SPELL.getValue()) {
            if (!spellUse(consumeCard.getConfigId(), targetIdList, consumeCard)) {
                return false;
            }
        } else if (consumeCard.getCardType() == eFoxCardType.SORCERY.getValue()) {
            if (!sorceryUse(consumeCard.getConfigId(), targetIdList, consumeCard)) {
                return false;
            }
        } else if (consumeCard.getCardType() == eFoxCardType.MAGIC_WEAPON.getValue()) {
            magicWeaponUse(consumeCard);
        }
        return true;
    }

    public void endShopping() {
        gameData.setIsShopping(false);
        gameData.setInBattle(false);

        dealCardEffect(eFoxCardTriggerType.SHOPPING_END);

        nextRound();
    }

    public void nextRound() {
        gameData.setRoundLastShowCombo(0);
        gameData.setRoundLimitEffectCard(new HashSet<>());
        gameData.setRoundShowCombos(new HashSet<>());
        gameData.setDamageAmount(BigInteger.ZERO);
        resetRemainHandCards();

        gameData.setRound(gameData.getRound() + 1);
        gameData.setShowCardTimes(gameData.getShowCardTimesMax());
        gameData.setDropCardTimes(gameData.getDropCardTimesMax());

        UserFoxCardMonster monster = gameData.getMonster(gameData.getRound());
        gameData.setBossLimitEffect(monster.getBossLimitEffect());

        reCalcCardOpTimes();
    }


    public int getHistoryComboShowTimes(int comboId) {
        List<Integer> list = gameData.getComboInfoMap().get(comboId);
        if (list == null) {
            return 0;
        }
        return list.get(1);
    }

    public int getComboLevel(int comboId) {
        List<Integer> list = gameData.getComboInfoMap().get(comboId);
        if (list == null) {
            return 0;
        }
        return list.get(0);
    }

    public void startBattle() {
        if (!gameData.getInBattle()) {
            gameData.setInBattle(true);

            reCalcCardOpTimes();
            resetRemainHandCards();

            addHandCards();

            gameData.setShowCardTimes(gameData.getShowCardTimesMax());
            gameData.setDropCardTimes(gameData.getDropCardTimesMax());
            dealCardEffect(eFoxCardTriggerType.ROUND_START);
        }

    }

    public boolean isHeardHandCard(FoxHandCard handCard) {
        for (FoxEffectCard effectCard : gameData.getEffectCards()) {
            FoxCardEffectCardConfig effectCardConfig = config.getEffectCardConfigMap().get(effectCard.getConfigId());
            if (effectCardConfig == null) {
                continue;
            }
            if (effectCardConfig.getType() == 21) {//所有卡牌视为人头牌
                return true;
            }
        }
        if (config.isNotPointAndNotColorHandCard(handCard.getReinforceEffect())) {
            return false;
        }
        return config.isHeadCard(handCard.getPoint());
    }

    public boolean isNumberHandCard(FoxHandCard handCard) {
        for (FoxEffectCard effectCard : gameData.getEffectCards()) {
            FoxCardEffectCardConfig effectCardConfig = config.getEffectCardConfigMap().get(effectCard.getConfigId());
            if (effectCardConfig == null) {
                continue;
            }
            if (effectCardConfig.getType() == 51) {//所有卡牌视为数字牌
                return true;
            }
        }
        if (config.isNotPointAndNotColorHandCard(handCard.getReinforceEffect())) {
            return false;
        }
        return config.isNumberCard(handCard.getPoint());
    }

    public int getHandCardNowPoint(FoxHandCard handCard) {
        if (config.isNotPointAndNotColorHandCard(handCard.getReinforceEffect())) {
            return 0;
        }
        return handCard.getPoint();
    }

    public int getMaxLevelComboId() {
        List<JSONObject> comboSortList = new ArrayList<>();
        for (Map.Entry<Integer, List<Integer>> entry : gameData.getComboInfoMap().entrySet()) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("comboId", entry.getKey());
            jsonObject.put("level", entry.getValue().get(0));
            jsonObject.put("showNum", entry.getValue().get(1));
            comboSortList.add(jsonObject);
        }
        comboSortList.sort((o1, o2) -> {
            int level1 = o1.getIntValue("level");
            int level2 = o2.getIntValue("level");
            int compare = Integer.compare(level2, level1);
            if (compare == 0) {
                int comboId1 = o1.getIntValue("comboId");
                int comboId2 = o2.getIntValue("comboId");
                compare = Integer.compare(comboId1, comboId2);
            }
            return compare;
        });

        JSONObject result = comboSortList.get(0);
        return result.getIntValue("comboId");
    }

    public int getCouponAddValue(int couponType) {
        int addValue = 0;
        for (Integer coupon : gameData.getCoupons()) {
            FoxCardCouponConfig couponConfig = config.getCouponConfigMap().get(coupon);
            if (couponConfig != null && couponConfig.getType() == couponType) {
                addValue += couponConfig.getParamList().get(1);
            }
        }
        return addValue;
    }

    /**
     * 重新计算出牌次数和弃牌次数,手牌上限,boss限制最优先,最后加加减减,最低减到0
     */
    public void reCalcCardOpTimes() {
        int oriShowMax = gameData.getShowCardTimesMax();
        int oriDropMax = gameData.getDropCardTimesMax();

        int initShowMax = config.getGameInitParams().get(1);
        int initDropMax = config.getGameInitParams().get(2);
        int initHandsLimit = config.getGameInitParams().get(3);

        FoxCardChapterConfig chapterConfig = config.getChapterConfigMap().get(gameData.getStageId());
        if (chapterConfig.getType() == 1) {
            //额外出牌次数|额外弃牌次数|额外金币|额外的小丑牌数量|额外的出牌次数
            initShowMax += chapterConfig.getShowTimesAdd();
            initDropMax += chapterConfig.getDropTimesAdd();
        }

        for (Integer couponId : gameData.getCoupons()) {
            FoxCardCouponConfig couponConfig = config.getCouponConfigMap().get(couponId);
            if (couponConfig != null && couponConfig.getType() == eFoxCardCouponType.COUPON2.getValue()) {
                if (couponConfig.getParamList().get(0) == 1) {//加出牌次数
                    initShowMax += couponConfig.getParamList().get(1);
                } else if (couponConfig.getParamList().get(0) == 2) {//加弃牌次数
                    initDropMax += couponConfig.getParamList().get(1);
                } else if (couponConfig.getParamList().get(0) == 3) {//加手牌上限
                    initHandsLimit += couponConfig.getParamList().get(1);
                }
            }
        }

        for (FoxEffectCard effectCard : gameData.getEffectCards()) {
            FoxCardEffectCardConfig effectConfig = config.getEffectCardConfigMap().get(effectCard.getConfigId());
            if (effectConfig == null) {
                continue;
            }

            if (effectConfig.getType() == 16) {
                int addHandCardLimit = Integer.valueOf(effectConfig.getParamsList().get(0));
                int decHandCardLimit = Integer.valueOf(effectConfig.getParamsList().get(1));
                int addShowCardTimes = Integer.valueOf(effectConfig.getParamsList().get(2));
                int decShowCardTimes = Integer.valueOf(effectConfig.getParamsList().get(3));
                int addDropCardTimes = Integer.valueOf(effectConfig.getParamsList().get(4));
                int decDropCardTimes = Integer.valueOf(effectConfig.getParamsList().get(5));

                if (addHandCardLimit > 0) {
                    initHandsLimit += addHandCardLimit;
                }

                if (addShowCardTimes > 0) {
                    initShowMax += addShowCardTimes;
                }

                if (addDropCardTimes > 0) {
                    initDropMax += addDropCardTimes;
                }

                if (decHandCardLimit > 0) {
                    initHandsLimit -= decHandCardLimit;
                }

                if (decShowCardTimes > 0) {
                    initShowMax -= decShowCardTimes;
                }

                if (decDropCardTimes > 0) {
                    initDropMax -= decDropCardTimes;
                }
            } else if (effectConfig.getType() == 17) {
                int decHandCardLimit = Integer.valueOf(effectConfig.getParamsList().get(4));
                int decDropCardTimes = Integer.valueOf(effectConfig.getParamsList().get(5));

                if (decHandCardLimit > 0) {
                    initHandsLimit -= decHandCardLimit;
                }

                if (decDropCardTimes > 0) {
                    initDropMax -= decDropCardTimes;
                }

            } else if (effectConfig.getType() == 14) {
                int roundHandCardLimit = Integer.valueOf(effectConfig.getParamsList().get(3));
                if (roundHandCardLimit > 0) {
                    long roundRemain = 0;
                    if (effectCard.getEffectValues().size() > 0){
                        roundRemain = effectCard.getEffectValues().get(0);
                    }
                    long limitAdd = roundRemain * roundHandCardLimit;
                    initHandsLimit += (int) limitAdd;
                }
            }
        }

        for (Integer labelId : gameData.getSkipLabelHaves()) {
            FoxCardLabelConfig labelConfig = config.getLabelConfigMap().get(labelId);
            if (labelConfig == null) {
                continue;
            }
            if (labelConfig.getType() == 11) {
                int addHandCardLimit = labelConfig.getParamList().get(0);
                initHandsLimit += addHandCardLimit;
            }
        }

        initShowMax = Math.max(0, initShowMax);
        initDropMax = Math.max(0, initDropMax);
        initHandsLimit = Math.max(1, initHandsLimit);

        if (gameData.getBossLimitEffect() > 0) {
            FoxCardLimitConfig limitConfig = config.getLimitConfigMap().get(gameData.getBossLimitEffect());
            if (limitConfig != null) {
                if (limitConfig.getType() == eFoxCardLimitType.LIMIT3.getValue()) {
                    initDropMax = 0;
                } else if (limitConfig.getType() == eFoxCardLimitType.LIMIT4.getValue()) {
                    int limitMax = limitConfig.getParamsList().get(0);
                    initShowMax = limitMax;
                } else if (limitConfig.getType() == eFoxCardLimitType.LIMIT6.getValue()) {
                    int decNum = limitConfig.getParamsList().get(0);
                    initHandsLimit -= decNum;
                }
            }
        }

        gameData.setShowCardTimesMax(initShowMax);
        gameData.setDropCardTimesMax(initDropMax);
        gameData.setHandCardNumLimit(initHandsLimit);

        int showOffset = gameData.getShowCardTimesMax() - oriShowMax;
        gameData.setShowCardTimes(Math.max(1, gameData.getShowCardTimes() + showOffset));

        int dropOffset = gameData.getDropCardTimesMax() - oriDropMax;
        gameData.setDropCardTimes(Math.max(0, gameData.getDropCardTimes() + dropOffset));

    }
}
